From 8f39d5d18a9354f9c410ec7367631a9c2dd5d665 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 10 Apr 2026 19:48:00 -0600 Subject: [PATCH 1/2] GIT: Set EOL attributes for text files - Visual Studio project/solution files are always CRLF - all others are auto --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9268382 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.vcxproj text=crlf +*.vcxproj.filters text=crlf +*.sln text=crlf +* text=auto From ad5d8dcbeb96c9c193f01d5f1f594dee32b899a0 Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 10 Apr 2026 19:50:36 -0600 Subject: [PATCH 2/2] GIT: Normalize line endings --- .gitignore | 524 +- README.md | 164 +- config11/config11.c | 232 +- config11/config11.txt | 34 +- converters/asc/asc.c | 216 +- converters/asc/asc.txt | 28 +- converters/decsys/decsys.c | 138 +- converters/dtos8cvt/dtos8cvt.c | 136 +- converters/dtos8cvt/dtos8cvt.txt | 24 +- converters/gt7cvt/gt7cvt.c | 194 +- converters/hpconvert/hpconvert.c | 850 +- converters/imd2dsk/imd2dsk.c | 1416 +- converters/indent/indent.c | 244 +- converters/littcvt/littcvt.c | 142 +- converters/m8376/m8376.c | 148 +- converters/mt2tpc/mt2tpc.c | 178 +- converters/mtcvt23/mtcvtv23.c | 166 +- converters/mtcvt23/mtcvtv23.txt | 68 +- converters/mtcvtfix/mtcvtfix.c | 192 +- converters/mtcvtodd/mtcvtodd.c | 178 +- converters/noff/noff.c | 136 +- converters/sfmtcvt/sfmtcvt.c | 236 +- converters/strrem/strrem.c | 144 +- converters/strsub/strsub.c | 148 +- converters/tar2mt/tar2mt.c | 184 +- converters/tar2mt/tar2mt.txt | 48 +- converters/tp512cvt/tp512cvt.c | 152 +- converters/tpc2mt/tpc2mt.c | 168 +- crossassemblers/hpasm/hpasm.c | 1860 +- crossassemblers/macro1/macro1.c | 6136 +- crossassemblers/macro11/.indent.pro | 90 +- crossassemblers/macro11/obj2bin/.gitignore | 8 +- crossassemblers/macro7/macro7.c | 6166 +- crossassemblers/macro8x/macro8x.c | 8502 +- extracters/backup/backup.c | 1272 +- extracters/backup/backup.h | 160 +- extracters/backup/backup.txt | 182 +- extracters/ckabstape/ckabstape.c | 854 +- extracters/mmdir/mmdir.c | 210 +- extracters/mtdump/mtdump.c | 296 +- extracters/mtdump/mtdump.txt | 100 +- extracters/ods2/00INSTALL.TXT | 182 +- extracters/ods2/00README.txt | 350 +- extracters/ods2/00_old_FAQ.txt | 350 +- extracters/ods2/Makefile | 2 +- extracters/ods2/access.c | 3448 +- extracters/ods2/access.h | 322 +- extracters/ods2/cache.c | 902 +- extracters/ods2/cache.h | 118 +- extracters/ods2/cmddef.h | 412 +- extracters/ods2/compat.c | 900 +- extracters/ods2/compat.h | 182 +- extracters/ods2/copycmd.c | 2966 +- extracters/ods2/createcmd.c | 1346 +- extracters/ods2/debug.c | 250 +- extracters/ods2/debug.h | 30 +- extracters/ods2/deletecmd.c | 358 +- extracters/ods2/descrip.h | 98 +- extracters/ods2/device.c | 292 +- extracters/ods2/device.h | 136 +- extracters/ods2/diffcmd.c | 308 +- extracters/ods2/dircmd.c | 1642 +- extracters/ods2/direct.c | 2398 +- extracters/ods2/direct.h | 78 +- extracters/ods2/dismountcmd.c | 128 +- extracters/ods2/en_us.msg | 1496 +- extracters/ods2/extendcmd.c | 176 +- extracters/ods2/f11def.h | 614 +- extracters/ods2/fibdef.h | 76 +- extracters/ods2/helpcmd.c | 2052 +- extracters/ods2/initialcmd.c | 442 +- extracters/ods2/initvol.c | 2808 +- extracters/ods2/initvol.h | 198 +- extracters/ods2/makefile.generic | 386 +- extracters/ods2/makefile.nt | 74 +- extracters/ods2/makefile.solaris | 30 +- extracters/ods2/makefile.tru64 | 54 +- extracters/ods2/makefile.unix | 22 +- extracters/ods2/makefile.unixdefs | 156 +- extracters/ods2/mountcmd.c | 366 +- extracters/ods2/ods2.c | 2726 +- extracters/ods2/ods2.h | 236 +- extracters/ods2/ods2.rc | 218 +- extracters/ods2/phyio.h | 136 +- extracters/ods2/phynt.c | 2452 +- extracters/ods2/phyos2.c | 494 +- extracters/ods2/phyunix.c | 904 +- extracters/ods2/phyvhd.c | 916 +- extracters/ods2/phyvhd.h | 56 +- extracters/ods2/phyvirt.c | 2166 +- extracters/ods2/phyvirt.h | 128 +- extracters/ods2/phyvms.c | 948 +- extracters/ods2/renamecmd.c | 428 +- extracters/ods2/resource.h | 32 +- extracters/ods2/rms.c | 6370 +- extracters/ods2/rms.h | 796 +- extracters/ods2/scsidefs.h | 576 +- extracters/ods2/searchcmd.c | 1276 +- extracters/ods2/setcmd.c | 996 +- extracters/ods2/showcmd.c | 1598 +- extracters/ods2/spawncmd.c | 230 +- extracters/ods2/stsdef.h | 112 +- extracters/ods2/sysmsg.c | 1474 +- extracters/ods2/sysmsg.h | 312 +- extracters/ods2/termio.c | 694 +- extracters/ods2/update.c | 2308 +- extracters/ods2/version.h | 146 +- extracters/ods2/vhd/blk_uuid.h | 400 +- extracters/ods2/vhd/libvhd.c | 8404 +- extracters/ods2/vhd/libvhd.h | 726 +- extracters/ods2/vhd/relative-path.c | 888 +- extracters/ods2/vhd/relative-path.h | 118 +- extracters/ods2/vhd/vhd.h | 456 +- extracters/ods2/vms_msgdefs.h | 117038 +++++++++--------- extracters/ods2/vms_msgdump.en_us.msg | 6490 +- extracters/ods2/vmsfile.c | 356 +- extracters/ods2/vmsfile.h | 104 +- extracters/ods2/vmstime.c | 2318 +- extracters/ods2/vmstime.h | 384 +- extracters/ods2/win9x.c | 378 +- extracters/ods2/winaspi.c | 456 +- extracters/ods2/winfile.c | 366 +- extracters/ods2/winfile.h | 104 +- extracters/ods2/wnaspi32.h | 782 +- extracters/rawcopy/RawCopy.c | 100 +- extracters/rawcopy/RawCopy.txt | 34 +- extracters/sdsdump/sdsdump.c | 190 +- extracters/tpdump/tpdump.c | 368 +- putr/putr.txt | 1860 +- 129 files changed, 114760 insertions(+), 114760 deletions(-) diff --git a/.gitignore b/.gitignore index 2ce195a..5196ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,262 +1,262 @@ -\#* -*~ -*.bak -*.d -*.dep -*.o -*.orig -*.vhd -*.iso -pc/* -*.simh_dsk -*.swp -*.exe -*.lst - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -\~AutoRecover.* - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -.gdb_history +\#* +*~ +*.bak +*.d +*.dep +*.o +*.orig +*.vhd +*.iso +pc/* +*.simh_dsk +*.swp +*.exe +*.lst + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +\~AutoRecover.* + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +.gdb_history diff --git a/README.md b/README.md index 763f50b..f69b2fa 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,82 @@ -#simh support/migration tools. - -The tools are organized into categories, each of which has a subdirectory. - -For consistency, each tool has its own subdirectory, even if it only contains a single file. -(READMEs for tools without them are welcome) - - -## Configuration support - -Tools to assist with configuring a simulator. - -Directory | Contents ----- | ---- -config11 | Calculate the floating address space layout of a PDP-11 or VAX. - -## Converters - -Tools that convert/support simulator data file formats - -Directory | Contents ----- | ---- -asc | Convert ASCII file line endings -decsys | Convert decimal listing file to a DECtape file -dtos8cvt | Convert a PDP-8 DECtape image from OS/8 format to simulator format -gt7cvt | Convert a gt7 magtape dump to a SIMH magtape -hpconvert | Convert an HP disc image between SIMH and HPDrive formats -imd2dsk | Convert an ImageDisk (IMD) file to DSK (pure data) -indent | Convert simulator sources to 4-column tabs -lbn2pbn | Logical-to-physical (and reverse) converter for single-sided floppy disk images -littcvt | Remove density maker from litt format tapes -m8376 | Assembles 8 PROM files into a 32bit binary file -mt2tpc | Convert a simh simulated magtape to TPC format -mtcvtfix | Fix a SIMH magtape containing a misread EOF -mtcvtodd | Convert an E11 magtape (with odd record sizes) to SIMH -mtcvtv23 | Convert a tape image in .TPC format to SIMH (.tap) -noff | Remove (formfeed, \f) from a source listing -sfmtcvt | Convert a Motorola S format PROM dump to a binary file -strrem | Remove a string from each line of a file -strsub | Substitute a string in each line of a file -tar2mt | Convert a tar file to a simulated 8192B blocked magtape -tp512cvt | Convert a tp data file to a simulated 512B blocked magtape -tpc2mt | Convert a TPC simulated magtape to simh format - -## Cross-assemblers - -Cross-assemblers for various machine architectures - -Directory | Contents ----- | ---- -hpasm | Assembler for the HP2100 -macro1 | Assembler for the PDP-1 -macro7 | Assembler for the PDP-7 -macro8x | Assembler for the PDP-8 -macro11 | Assembler for the PDP-11. Synchronized from git `https://gitlab.com/Rhialto/macro11.git`; most recently from tag `macro11-v0.7.2`. - -## Extracters - -Data extraction tools - -Except as noted, all read SIMH tape container format. - -Directory | Contents ----- | ---- -backup | Extract files from a TOPS-10 backup tape -ckabstape | Disassemble 18-bit binary paper tape -mmdir | List directory of Interdata MDM tape -mtdump | Dump the record structure of a SIMH, E11, TPC, or P7B -ods2 | Directory, Copy & Search commands for VMS ODS2 disk images -rawcopy | Create SIMH disk image from physical media on RAW device. -rstsflx | Manipulate PDP11 RSTS file systems. -sdsdump | Disassemble SDS SDS paper tape -tpdump | Dump files on IBM 1401 tape - -## File systems - -Provide access to a foreign files system from a host machine. - -Directory | Contents ----- | ---- -putr | Read (and write some) DEC filesystems from PCs - +#simh support/migration tools. + +The tools are organized into categories, each of which has a subdirectory. + +For consistency, each tool has its own subdirectory, even if it only contains a single file. +(READMEs for tools without them are welcome) + + +## Configuration support + +Tools to assist with configuring a simulator. + +Directory | Contents +---- | ---- +config11 | Calculate the floating address space layout of a PDP-11 or VAX. + +## Converters + +Tools that convert/support simulator data file formats + +Directory | Contents +---- | ---- +asc | Convert ASCII file line endings +decsys | Convert decimal listing file to a DECtape file +dtos8cvt | Convert a PDP-8 DECtape image from OS/8 format to simulator format +gt7cvt | Convert a gt7 magtape dump to a SIMH magtape +hpconvert | Convert an HP disc image between SIMH and HPDrive formats +imd2dsk | Convert an ImageDisk (IMD) file to DSK (pure data) +indent | Convert simulator sources to 4-column tabs +lbn2pbn | Logical-to-physical (and reverse) converter for single-sided floppy disk images +littcvt | Remove density maker from litt format tapes +m8376 | Assembles 8 PROM files into a 32bit binary file +mt2tpc | Convert a simh simulated magtape to TPC format +mtcvtfix | Fix a SIMH magtape containing a misread EOF +mtcvtodd | Convert an E11 magtape (with odd record sizes) to SIMH +mtcvtv23 | Convert a tape image in .TPC format to SIMH (.tap) +noff | Remove (formfeed, \f) from a source listing +sfmtcvt | Convert a Motorola S format PROM dump to a binary file +strrem | Remove a string from each line of a file +strsub | Substitute a string in each line of a file +tar2mt | Convert a tar file to a simulated 8192B blocked magtape +tp512cvt | Convert a tp data file to a simulated 512B blocked magtape +tpc2mt | Convert a TPC simulated magtape to simh format + +## Cross-assemblers + +Cross-assemblers for various machine architectures + +Directory | Contents +---- | ---- +hpasm | Assembler for the HP2100 +macro1 | Assembler for the PDP-1 +macro7 | Assembler for the PDP-7 +macro8x | Assembler for the PDP-8 +macro11 | Assembler for the PDP-11. Synchronized from git `https://gitlab.com/Rhialto/macro11.git`; most recently from tag `macro11-v0.7.2`. + +## Extracters + +Data extraction tools + +Except as noted, all read SIMH tape container format. + +Directory | Contents +---- | ---- +backup | Extract files from a TOPS-10 backup tape +ckabstape | Disassemble 18-bit binary paper tape +mmdir | List directory of Interdata MDM tape +mtdump | Dump the record structure of a SIMH, E11, TPC, or P7B +ods2 | Directory, Copy & Search commands for VMS ODS2 disk images +rawcopy | Create SIMH disk image from physical media on RAW device. +rstsflx | Manipulate PDP11 RSTS file systems. +sdsdump | Disassemble SDS SDS paper tape +tpdump | Dump files on IBM 1401 tape + +## File systems + +Provide access to a foreign files system from a host machine. + +Directory | Contents +---- | ---- +putr | Read (and write some) DEC filesystems from PCs + diff --git a/config11/config11.c b/config11/config11.c index 229436c..29e37ee 100644 --- a/config11/config11.c +++ b/config11/config11.c @@ -1,116 +1,116 @@ -/* Program to configure the floating address space of a PDP-11 or VAX - - Copyright (c) 2002, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#include -#include - -#define RANK_LNT 34 - -int csr, i, j; -unsigned int rank, num; -char *cp, *ocp, inp[100]; -unsigned char numctl[RANK_LNT]; -unsigned char modtab[RANK_LNT] = { - 0X07, 0X0f, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, - 0X07, 0X07, 0X07, 0X0f, 0X07, 0X07, 0X0f, 0X07, - 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X0f, - 0X07, 0X03, 0X1f, 0X0f, 0X0f, 0X03, 0X0F, 0x0F, - 0x1F, 0X1F }; -unsigned int fixtab[RANK_LNT] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0774400, 0770460, 0, - 0, 0777170, 0, 0772410, 0, 0, 0, 0, - 0774440, 0772150, 0, 0, 0, 0774500, 0, 0, - 0, 0 }; -char *namtab[RANK_LNT] = { - "DJ11", "DH11", "DQ11", "DU11", "DUP11", "LK11A", "DMC11", "DZ11", - "KMC11", "LPP11", "VMV21", "VMV31", "DWR70", "RL11", "LPA11K", "KW11C", - "rsvd", "RX11", "DR11W", "DR11B", "DMP11", "DPV11", "ISB11", "DMV11", - "DEUNA", "UDA50", "DMF32", "KMS11", "VS100", "TK50", "KMV11", "DHV11", - "DMZ32", "CP132" }; - -int main (int argc, char *argv[]) -{ -(void) argc; -(void) argv; - -for ( ;; ) { - for (i = 0; i < RANK_LNT; i++) numctl[i] = 0; - printf ("Enter configuration data\n"); - for ( ;; ) { - printf ("Name:\t"); - if (fgets (inp, sizeof (inp), stdin) == NULL) return 0; - for (cp = inp; *cp != 0; cp++) if (*cp == '\r' || *cp == '\n') *cp = '\0'; - if (*inp == 0) break; - for (cp = inp; *cp != 0; cp++) *cp = toupper (*cp); - for (rank = 0; rank < RANK_LNT; rank++) { - if (strcmp (inp, namtab[rank]) == 0) break; } - if (rank >= RANK_LNT) { - printf ("Unknown controller, valid names are:"); - for (i = 0; i < RANK_LNT; i++) { - if ((i & 07) == 0) printf ("\n"); - printf (" %s", namtab[i]); } - printf ("\n"); - continue; } - printf ("Number:\t"); - fgets (inp, sizeof (inp), stdin); - errno = 0; - num = strtoul (inp, &ocp, 10); - if (errno || (inp == ocp)) { - printf ("Input error\n"); - continue; } - if (num > 8) { - printf ("Too many controllers\n"); - continue; } - numctl[rank] = num; - } - - printf ("\nRank\tName\tCtrl#\t CSR\n\n"); - csr = 0760010; - for (i = 0; i < RANK_LNT; i++) { - if (numctl[i] == 0) { - printf (" %02d\t%s\tgap\t%06o\n", i+1, namtab[i], csr); } - else { - if (fixtab[i]) - printf (" %02d\t%s\t 1\t%06o*\n", i+1, namtab[i], fixtab[i]); - else { - printf (" %02d\t%s\t 1\t%06o\n", i+1, namtab[i], csr); - csr = (csr + modtab[i] + 1) & ~modtab[i]; } - for (j = 1; j < numctl[i]; j++) { - printf ("\t\t %d\t%06o\n", j + 1, csr); - csr = (csr + modtab[i] + 1) & ~modtab[i]; } - printf (" %%\t\tgap\t%06o\n", csr); - } - if ((i + 1) < RANK_LNT) csr = (csr + modtab[i+1] + 1) & ~modtab[i+1]; - } - printf ("\n\n"); - } -return 0; -} +/* Program to configure the floating address space of a PDP-11 or VAX + + Copyright (c) 2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#include +#include + +#define RANK_LNT 34 + +int csr, i, j; +unsigned int rank, num; +char *cp, *ocp, inp[100]; +unsigned char numctl[RANK_LNT]; +unsigned char modtab[RANK_LNT] = { + 0X07, 0X0f, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, + 0X07, 0X07, 0X07, 0X0f, 0X07, 0X07, 0X0f, 0X07, + 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X07, 0X0f, + 0X07, 0X03, 0X1f, 0X0f, 0X0f, 0X03, 0X0F, 0x0F, + 0x1F, 0X1F }; +unsigned int fixtab[RANK_LNT] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0774400, 0770460, 0, + 0, 0777170, 0, 0772410, 0, 0, 0, 0, + 0774440, 0772150, 0, 0, 0, 0774500, 0, 0, + 0, 0 }; +char *namtab[RANK_LNT] = { + "DJ11", "DH11", "DQ11", "DU11", "DUP11", "LK11A", "DMC11", "DZ11", + "KMC11", "LPP11", "VMV21", "VMV31", "DWR70", "RL11", "LPA11K", "KW11C", + "rsvd", "RX11", "DR11W", "DR11B", "DMP11", "DPV11", "ISB11", "DMV11", + "DEUNA", "UDA50", "DMF32", "KMS11", "VS100", "TK50", "KMV11", "DHV11", + "DMZ32", "CP132" }; + +int main (int argc, char *argv[]) +{ +(void) argc; +(void) argv; + +for ( ;; ) { + for (i = 0; i < RANK_LNT; i++) numctl[i] = 0; + printf ("Enter configuration data\n"); + for ( ;; ) { + printf ("Name:\t"); + if (fgets (inp, sizeof (inp), stdin) == NULL) return 0; + for (cp = inp; *cp != 0; cp++) if (*cp == '\r' || *cp == '\n') *cp = '\0'; + if (*inp == 0) break; + for (cp = inp; *cp != 0; cp++) *cp = toupper (*cp); + for (rank = 0; rank < RANK_LNT; rank++) { + if (strcmp (inp, namtab[rank]) == 0) break; } + if (rank >= RANK_LNT) { + printf ("Unknown controller, valid names are:"); + for (i = 0; i < RANK_LNT; i++) { + if ((i & 07) == 0) printf ("\n"); + printf (" %s", namtab[i]); } + printf ("\n"); + continue; } + printf ("Number:\t"); + fgets (inp, sizeof (inp), stdin); + errno = 0; + num = strtoul (inp, &ocp, 10); + if (errno || (inp == ocp)) { + printf ("Input error\n"); + continue; } + if (num > 8) { + printf ("Too many controllers\n"); + continue; } + numctl[rank] = num; + } + + printf ("\nRank\tName\tCtrl#\t CSR\n\n"); + csr = 0760010; + for (i = 0; i < RANK_LNT; i++) { + if (numctl[i] == 0) { + printf (" %02d\t%s\tgap\t%06o\n", i+1, namtab[i], csr); } + else { + if (fixtab[i]) + printf (" %02d\t%s\t 1\t%06o*\n", i+1, namtab[i], fixtab[i]); + else { + printf (" %02d\t%s\t 1\t%06o\n", i+1, namtab[i], csr); + csr = (csr + modtab[i] + 1) & ~modtab[i]; } + for (j = 1; j < numctl[i]; j++) { + printf ("\t\t %d\t%06o\n", j + 1, csr); + csr = (csr + modtab[i] + 1) & ~modtab[i]; } + printf (" %%\t\tgap\t%06o\n", csr); + } + if ((i + 1) < RANK_LNT) csr = (csr + modtab[i+1] + 1) & ~modtab[i+1]; + } + printf ("\n\n"); + } +return 0; +} diff --git a/config11/config11.txt b/config11/config11.txt index a2915eb..0ab24a0 100644 --- a/config11/config11.txt +++ b/config11/config11.txt @@ -1,17 +1,17 @@ -Config11 is a program for calculating the floating address space layout of -a PDP-11 or VAX. - -Config11 is an interactive program. When started, it repeatedly asks for -the names of controllers and the numbers of instances: - -> config11 -Name: UDA50 -Number: 2 -Name: DZ11 -Number: 2 -Name: - -To end input, simply type ENTER to the NAME: prompt. Config11 will then -print out the complete floating address table for the known Unibus and -Qbus devices. - +Config11 is a program for calculating the floating address space layout of +a PDP-11 or VAX. + +Config11 is an interactive program. When started, it repeatedly asks for +the names of controllers and the numbers of instances: + +> config11 +Name: UDA50 +Number: 2 +Name: DZ11 +Number: 2 +Name: + +To end input, simply type ENTER to the NAME: prompt. Config11 will then +print out the complete floating address table for the known Unibus and +Qbus devices. + diff --git a/converters/asc/asc.c b/converters/asc/asc.c index 5532f37..8d31a9c 100644 --- a/converters/asc/asc.c +++ b/converters/asc/asc.c @@ -1,108 +1,108 @@ -/* This program converts delimited files to Windoze - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define LAST_ANY 0 -#define LAST_CR 1 -#define LAST_LF 2 -#define MD_WIN 0 -#define MD_UNIX 1 -#define MD_MAC 2 - -void puteol (int mode, FILE *of) -{ -if (mode != MD_UNIX) putc ('\r', of); -if (mode != MD_MAC) putc ('\n', of); -return; -} - -int main (int argc, char *argv[]) -{ -int i, k, mc, lastc; -int mode; -char *s, *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: asc -muw file [file...]\n"); - exit (0); } - -s = argv[1]; -if ((s != NULL) && (*s++ == '-')) { - ++argv; --argc; - switch (*s) { - case 'm': case 'M': - mode = MD_MAC; break; - case 'u': case 'U': - mode = MD_UNIX; break; - case 'w': case 'W': - mode = MD_WIN; break; - default: - fprintf (stderr, "Bad option %c\n", *s); - return 0; } - } -else mode = MD_WIN; - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".new"); - else strcat (oname, ".new"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (lastc = LAST_ANY;;) { - k = getc (ifile); - if (k == EOF) break; - mc = k & 0177; - if (mc && (mc != 0177)) { - if (mc == 015) { - if (lastc == LAST_CR) puteol (mode, ofile); - lastc = LAST_CR; } - else if (mc == 012) { - puteol (mode, ofile); - lastc = LAST_LF; } - else { - if (lastc == LAST_CR) puteol (mode, ofile); - putc (mc, ofile); - lastc = LAST_ANY; } - } - } - if (lastc == LAST_CR) puteol (mode, ofile); - fclose (ifile); - fclose (ofile); - } -return 0; -} +/* This program converts delimited files to Windoze + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define LAST_ANY 0 +#define LAST_CR 1 +#define LAST_LF 2 +#define MD_WIN 0 +#define MD_UNIX 1 +#define MD_MAC 2 + +void puteol (int mode, FILE *of) +{ +if (mode != MD_UNIX) putc ('\r', of); +if (mode != MD_MAC) putc ('\n', of); +return; +} + +int main (int argc, char *argv[]) +{ +int i, k, mc, lastc; +int mode; +char *s, *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: asc -muw file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case 'm': case 'M': + mode = MD_MAC; break; + case 'u': case 'U': + mode = MD_UNIX; break; + case 'w': case 'W': + mode = MD_WIN; break; + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; } + } +else mode = MD_WIN; + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (lastc = LAST_ANY;;) { + k = getc (ifile); + if (k == EOF) break; + mc = k & 0177; + if (mc && (mc != 0177)) { + if (mc == 015) { + if (lastc == LAST_CR) puteol (mode, ofile); + lastc = LAST_CR; } + else if (mc == 012) { + puteol (mode, ofile); + lastc = LAST_LF; } + else { + if (lastc == LAST_CR) puteol (mode, ofile); + putc (mc, ofile); + lastc = LAST_ANY; } + } + } + if (lastc == LAST_CR) puteol (mode, ofile); + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/asc/asc.txt b/converters/asc/asc.txt index bb56baa..97cdcfb 100644 --- a/converters/asc/asc.txt +++ b/converters/asc/asc.txt @@ -1,14 +1,14 @@ -ASC is a simple utility for converting text files (delimited with -\r, \n, or \r\n) to Windows format (\r\n delimited), Unix format -(\n delimited) or Mac format (\r delimited). - -ASC is invoked from a DOS prompt with the command: - - > ASC {-m|u|w} file1 file2 ... - -The optional switch specifies Mac processing (-m), Unix processing -(-u), or Windows processing (-w). If no switch is specified, Windows -processing is performed. - -Each file is processed in turn. If the file is name.ext, the converted -file is name.new. +ASC is a simple utility for converting text files (delimited with +\r, \n, or \r\n) to Windows format (\r\n delimited), Unix format +(\n delimited) or Mac format (\r delimited). + +ASC is invoked from a DOS prompt with the command: + + > ASC {-m|u|w} file1 file2 ... + +The optional switch specifies Mac processing (-m), Unix processing +(-u), or Windows processing (-w). If no switch is specified, Windows +processing is performed. + +Each file is processed in turn. If the file is name.ext, the converted +file is name.new. diff --git a/converters/decsys/decsys.c b/converters/decsys/decsys.c index 5eba4cc..a2371c0 100644 --- a/converters/decsys/decsys.c +++ b/converters/decsys/decsys.c @@ -1,69 +1,69 @@ -/* This program converts a decimal listing to a DECtape file - - Copyright (c) 1993-2003, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ -int i, j, k, word; -char *ppos, oname[256]; -int fill[256] = { 0 }; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".dtp"); - else strcat (oname, ".dtp"); - ifile = fopen (argv[i], "r"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fwrite (fill, sizeof (int), 256, ofile); - for (j = 0;; j++) { - k = fscanf (ifile, "%d", &word); - if (k == EOF) break; -// printf ("%06o\n", word); - fwrite (&word, sizeof (int), 1, ofile); - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts a decimal listing to a DECtape file + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ +int i, j, k, word; +char *ppos, oname[256]; +int fill[256] = { 0 }; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".dtp"); + else strcat (oname, ".dtp"); + ifile = fopen (argv[i], "r"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fwrite (fill, sizeof (int), 256, ofile); + for (j = 0;; j++) { + k = fscanf (ifile, "%d", &word); + if (k == EOF) break; +// printf ("%06o\n", word); + fwrite (&word, sizeof (int), 1, ofile); + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/dtos8cvt/dtos8cvt.c b/converters/dtos8cvt/dtos8cvt.c index 0d6b495..c811980 100644 --- a/converters/dtos8cvt/dtos8cvt.c +++ b/converters/dtos8cvt/dtos8cvt.c @@ -1,68 +1,68 @@ -/* This program converts an OS8 DECtape image to simulator format - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define BLKSIZ 129 -int main (int argc, char *argv[]) -{ -int i, k; -unsigned short buf[BLKSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".dt8"); - else strcat (oname, ".dt8"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (;;) { - k = fread (buf, sizeof (short), BLKSIZ - 1, ifile); - if (k == 0) break; - for ( ; k < BLKSIZ; k++) buf[k] = 0; - fwrite (buf, sizeof (short), BLKSIZ, ofile); - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts an OS8 DECtape image to simulator format + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define BLKSIZ 129 +int main (int argc, char *argv[]) +{ +int i, k; +unsigned short buf[BLKSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) strcpy (ppos, ".dt8"); + else strcat (oname, ".dt8"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (;;) { + k = fread (buf, sizeof (short), BLKSIZ - 1, ifile); + if (k == 0) break; + for ( ; k < BLKSIZ; k++) buf[k] = 0; + fwrite (buf, sizeof (short), BLKSIZ, ofile); + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/dtos8cvt/dtos8cvt.txt b/converters/dtos8cvt/dtos8cvt.txt index e4febaa..8421421 100644 --- a/converters/dtos8cvt/dtos8cvt.txt +++ b/converters/dtos8cvt/dtos8cvt.txt @@ -1,13 +1,13 @@ -dtos8cvt converts a PDP-8 DECtape image from OS/8 format to simulator format. - -OS/8 only uses 128 words out of 129 in a PDP-8 DECtape block. The simulator -requires all 129 words, in order to simulate the actions of the hardware, and -to deal with oddball software systems such as the PDP-8 Disk Monitor. - -dtos8cvt is invoked by - - dtos8cvt file1 file2 file3... - -Each file in turn is converted from OS/8 format to simulator format. The -input file can have any extension; the converted file will have a .dt8 +dtos8cvt converts a PDP-8 DECtape image from OS/8 format to simulator format. + +OS/8 only uses 128 words out of 129 in a PDP-8 DECtape block. The simulator +requires all 129 words, in order to simulate the actions of the hardware, and +to deal with oddball software systems such as the PDP-8 Disk Monitor. + +dtos8cvt is invoked by + + dtos8cvt file1 file2 file3... + +Each file in turn is converted from OS/8 format to simulator format. The +input file can have any extension; the converted file will have a .dt8 extension. \ No newline at end of file diff --git a/converters/gt7cvt/gt7cvt.c b/converters/gt7cvt/gt7cvt.c index 690728d..e0e7f95 100644 --- a/converters/gt7cvt/gt7cvt.c +++ b/converters/gt7cvt/gt7cvt.c @@ -1,97 +1,97 @@ -/* This program converts a gt7 magtape dump to a SIMH magtape - - Copyright (c) 2002, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -unsigned char fzero[4] = { 0 }; - -int dump_rec (FILE *of, int bc, char *buf) -{ -unsigned char buc[4]; - -if (((bc == 1) && (buf[0] == 0xF)) || - ((bc == 2) && (buf[0] == 0xF) && (buf[1] == 0xF))) { - fwrite (fzero, sizeof (char), 4, of); - return 1; } -buc[0] = bc & 0xFF; -buc[1] = (bc >> 8) & 0xFF; -buc[2] = (bc >> 16) & 0xFF; -buc[3] = (bc >> 24) & 0xFF; -fwrite (buc, sizeof (char), 4, of); -fwrite (buf, sizeof (char), (bc + 1) & ~1, of); -fwrite (buc, sizeof (char), 4, of); -return 0; -} - -int main (int argc, char *argv[]) -{ -int i, ch, bc, rc, fc; -unsigned char buf[FLPSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); - else strcat (oname, ".tap"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (bc = rc = fc = 0;;) { - ch = fgetc (ifile); - if (ch == EOF) break; - if (ch & 0x80) { - if (bc) { - if (dump_rec (ofile, bc, buf)) - printf ("End of file %d\n", ++fc); - else printf ("Record %d size %d\n", ++rc, bc); - } - bc = 0; } - buf[bc++] = ch & 0x3F; - } - fclose (ifile); - if (bc) dump_rec (ofile, bc, buf); - fwrite (fzero, sizeof (char), 4, ofile); - printf ("End of file %d\n", ++fc); - fclose (ofile); - } - -return 0; -} +/* This program converts a gt7 magtape dump to a SIMH magtape + + Copyright (c) 2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +unsigned char fzero[4] = { 0 }; + +int dump_rec (FILE *of, int bc, char *buf) +{ +unsigned char buc[4]; + +if (((bc == 1) && (buf[0] == 0xF)) || + ((bc == 2) && (buf[0] == 0xF) && (buf[1] == 0xF))) { + fwrite (fzero, sizeof (char), 4, of); + return 1; } +buc[0] = bc & 0xFF; +buc[1] = (bc >> 8) & 0xFF; +buc[2] = (bc >> 16) & 0xFF; +buc[3] = (bc >> 24) & 0xFF; +fwrite (buc, sizeof (char), 4, of); +fwrite (buf, sizeof (char), (bc + 1) & ~1, of); +fwrite (buc, sizeof (char), 4, of); +return 0; +} + +int main (int argc, char *argv[]) +{ +int i, ch, bc, rc, fc; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (bc = rc = fc = 0;;) { + ch = fgetc (ifile); + if (ch == EOF) break; + if (ch & 0x80) { + if (bc) { + if (dump_rec (ofile, bc, buf)) + printf ("End of file %d\n", ++fc); + else printf ("Record %d size %d\n", ++rc, bc); + } + bc = 0; } + buf[bc++] = ch & 0x3F; + } + fclose (ifile); + if (bc) dump_rec (ofile, bc, buf); + fwrite (fzero, sizeof (char), 4, ofile); + printf ("End of file %d\n", ++fc); + fclose (ofile); + } + +return 0; +} diff --git a/converters/hpconvert/hpconvert.c b/converters/hpconvert/hpconvert.c index b42a717..243bae8 100644 --- a/converters/hpconvert/hpconvert.c +++ b/converters/hpconvert/hpconvert.c @@ -1,425 +1,425 @@ -/* Convert an HP disc image between SIMH and HPDrive formats. - - Copyright (c) 2012, J. David Bryan - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of the author shall not be used - in advertising or otherwise to promote the sale, use or other dealings in - this Software without prior written authorization from the author. - - - HPDrive is a free program written by Ansgar Kueckes that uses a PC and a GPIB - card to emulate a variety of vintage Hewlett-Packard disc and tape drives - that interface to computers via the HP-IB. It is available from: - - http://www.hp9845.net/9845/projects/hpdrive/ - - This program converts an HP disc image from SIMH to HPDrive format or - vice-versa. This permits interchanging images between the two programs. - - Usage: hpconvert - - SIMH writes and reads disc images in little-endian format, regardless of the - byte order of the host. In addition, SIMH accesses images for the the 7905 - and 7906 drives in platter order (i.e., all tracks on heads 0 and 1, followed - by all tracks on heads 2 and 3) to improve locality of access. It accesses - images for the 7920, 7925, and all CS/80 drives in cylinder order. - - HPDrive writes and reads images in big-endian format, regardless of the byte - order of the host, and accesses all images in cylinder order. - - This program swaps each pair of bytes in the disc image. In addition, if the - image is precisely the size of a 7905 or 7906 drive (15,151,104 or 20,201,472 - bytes, respectively), the access order is restructured from platter to - cylinder, or vice-versa. - - Note that SIMH does not currently create full-size disc images unless the - last sector on the drive is written. Therefore, it is recommended that new - images be initialized using the RTE FORMT or SWTCH programs to ensure that - the image files are of the correct size for this program and HPDrive. - - This program creates a scratch file to store the converted image. Only when - conversion is complete is the original file deleted and the scratch file - renamed to the original file's name. Running the program twice on the same - file will return the file to its original configuration. - - To decide the mode used in a 7905 or 7906 image, the program examines the OS - signature at the start of the file and compares it against a list of known - operating systems. If the OS signature is not recognized, the program - terminates without altering the file. Signature checking is not performed on - images other than those for the 7905 and 7906; those images are byte-swapped - unconditionally. - - Signatures for these operating systems and compatible 7905/06 drives are - recognized: - - - HP 1000 RTE-IVB: 7906H ICD - - HP 1000 RTE-6/VM: 7906H ICD - - HP 3000 MPE: 7905A/7906A MAC - - The signatures are contained in the first four words at the start of each - disc image. In hex representation, they are: - - - RTE-IVB ICD: 676D 06C0 776B 0B40 (LDA HHIGH ; CMA,CCE ; STA HRCNT ; ERB) - - RTE-6/VM ICD: 676D 06C0 776B 0B40 (LDA HHIGH ; CMA,CCE ; STA HRCNT ; ERB) - - MPE MAC: 5359 5354 454D 2044 ("SYSTEM D") - - These represent the start of the boot extension for RTE and the start of the - system disc label for MPE. The boot extension machine instructions are: - - RTE-IVB ICD - ----------- - - 62000 LDA EQU 062000B - 72000 STA EQU 072000B - 01200 O0 EQU HSTRT-1400B - - 02600 063555 HSTRT ABS LDA+HHIGH-O0 (word 1) - 02601 003300 CMA,CCE (word 2) - 02602 073553 ABS STA+HRCNT-O0 (word 3) - 02603 005500 ERB (word 4) - - 02753 000000 HRCNT NOP - - 02755 077377 NW#DS OCT 77377 - 02755 HHIGH EQU NW#DS - - RTE-6/VM ICD - ------------ - - 062000 LDA EQU 062000B - 072000 STA EQU 072000B - 000000R O0 EQU HSTRT-1400B - - 01400 063555 HSTRT ABS LDA+HHIGH-O0 (word 1) - 01401 003300 CMA,CCE (word 2) - 01402 073553 ABS STA+HRCNT-O0 (word 3) - 01403 005500 ERB (word 4) - - 01553 000000 HRCNT NOP - - 01555 077377 NW#DS OCT 77377 - 001555R HHIGH EQU NW#DS - - And the disc label is: - - MPE MAC and CS/80 - ----------------- - - 00000 051531 LABEL ASC 6,SYSTEM DISC (word 1) - 00001 051524 (word 2) - 00002 042515 (word 3) - 00003 020104 (word 4) - 00004 044523 - 00005 041440 - -*/ - - -#include -#include - - -/* MSVC does not provide "stdbool.h" or "unistd.h" */ - -#if defined (_MSC_VER) - typedef int bool; - const int false = 0; - #define isatty _isatty -#else - #include - #include -#endif - - -#define TRACK_SIZE ( 1 * 1 * 48 * 256) -#define CYLINDER_SIZE ( 1 * 2 * 48 * 256) -#define HP7905_SIZE (411 * 3 * 48 * 256) -#define HP7906_SIZE (411 * 4 * 48 * 256) - - -int main (int argc, - char **argv) - -{ - const char *format [] = { "HPDrive", "SIMH" }; - - const char *signatures [] = { "\x67\x6D\x06\xC0\x77\x6B\x0B\x40", /* RTE ICD */ - "SYSTEM D" }; /* MPE */ - - #define SIGNATURE_SIZE sizeof (signatures [0]) - - const int signature_count = sizeof (signatures) / SIGNATURE_SIZE; - - FILE *fin, *fout; - size_t file_size, record_size; - char *name_in, *name_out; - char sig_fwd [SIGNATURE_SIZE], sig_rev [SIGNATURE_SIZE]; - char hold, cylinder [CYLINDER_SIZE]; - bool identified = false, reversed = false, debug = false; - int cyl, from_cyl, to_cyl, remap; - int platter, cylinder_size, hole_size; - unsigned long i; - - -/* Read the disc image filename. */ - - if (argc != 2) { - puts ("\nHPConvert version 1.1"); - puts ("\nUsage: hpconvert "); - return 1; - } - - name_in = argv [1]; - -/* Open the source image file. */ - - fin = fopen (name_in, "rb"); - - if (!fin) { - printf ("Error: cannot open %s\n", name_in); - return 1; - } - -/* Get the size of the image. */ - - fseek (fin, 0, SEEK_END); - file_size = ftell (fin); - - -/* The blocks of a 7905 or 7906 image (as determined by the image file size) - will need to be rearranged. Set "remap" to the number of surfaces that must - be remapped. */ - - if (file_size == HP7905_SIZE) - remap = 1; - else if (file_size == HP7906_SIZE) - remap = 2; - else - remap = 0; - - -/* If the image is a 7905 or 7906, it must be remapped it for the target system. - To do that, check the OS signature to determine if it is in SIMH or HPDrive - format, and set the "reversed" flag if the signature bytes in the image are - reversed (this implies a platter-to-cylinder remapping of a 7905 or 7906 - image. */ - - if (remap) { - rewind (fin); - file_size = fread (sig_fwd, 1, SIGNATURE_SIZE, fin); - - for (i = 0; i < SIGNATURE_SIZE; i = i + 2) { - sig_rev [i] = sig_fwd [i + 1]; - sig_rev [i + 1] = sig_fwd [i]; - } - - for (i = 0; i < signature_count && identified == false; i++) { - reversed = strncmp (sig_rev, signatures [i], SIGNATURE_SIZE) == 0; - identified = reversed || strncmp (sig_fwd, signatures [i], SIGNATURE_SIZE) == 0; - } - -/* If the signature cannot be identified, then we do not know how to remap it, - so report the problem and exit. */ - - if (identified == false) { - printf ("Error: 790%i image OS signature not recognized.\n", remap + 4); - fclose (fin); - return 1; - } - } - - -/* Generate a temporary filename for the converted image. */ - - name_out = tmpnam (NULL); - - if (name_out == NULL) { - puts ("Error: cannot generate a temporary filename."); - fclose (fin); - return 1; - } - -/* Create the temporary target image. */ - - fout = fopen (name_out, "wb"); - - if (!fout) { - printf ("Error: cannot create %s\n", name_out); - fclose (fin); - return 1; - } - -/* Report the conversion that will be performed. */ - - if (remap) - printf ("Converting and remapping 790%i %s disc image to %s format.\n", - remap + 4, - format [reversed], - format [!reversed]); - else - puts ("Converting disc image."); - - -/* Enable debugging output if stdout has been redirected to a file. */ - - debug = isatty (fileno (stdout)) == 0; - - -/* Copy the source to the target image while swapping each byte pair. If the - disc image is for a 7905 or 7906, remap from platter to cylinder mode (or - vice-versa if the source image is not reversed). - - In a platter-mode image, the upper platter tracks appear in order before the - lower platter tracks. For the 7906, the cylinder-head order of the tracks is - 0-0, 0-1, 1-0, 1-1, ..., 410-0, 410-1, 0-2, 0-3, 1-2, 1-3, ..., 410-2, 410-3. - The 7905 order is the same, except that head 3 tracks are omitted. - - In a cylinder-mode image, all tracks appear in cylinder-head order, i.e., - 0-0, 0-1, 0-2, 0-3, 1-0, 1-1, ..., 410-2, 410-3, for the 7906. - - Remapping is performed in two passes, corresponding to the two platters. In - the first pass, the source tracks corresponding to the upper platter are - spread (platter-to-cylinder) or condensed (cylinder-to-platter) as they are - copied to the target file. In the second pass, the source tracks - corresponding to the lower platter are interleaved (platter-to-cylinder) or - appended (cylinder-to-platter) as they are copied to the target file. - - In either case, the bytes of each 16-bit word are swapped before copying. -*/ - - rewind (fin); - -/* If the image is not a 7905/06, simply swap each pair of bytes until EOF. */ - - if (!remap) - while (!feof (fin)) { - record_size = fread (cylinder, 1, CYLINDER_SIZE, fin); - - for (i = 0; i < record_size; i = i + 2) { - hold = cylinder [i]; - cylinder [i] = cylinder [i + 1]; - cylinder [i + 1] = hold; - } - - fwrite (cylinder, 1, record_size, fout); - } - -/* If the image is a 7905/06, remap the tracks and swap pairs of bytes. Because - we know the disc type, we know the number of platters and cylinders present - in the image. */ - - else - for (platter = 0; platter < 2; platter++) { - -/* Calculate the number of bytes per cylinder for the current platter. The - upper platter always has two tracks per cylinder. The lower platter has - either one (7905) or two (7906) tracks per cylinder. */ - - if (platter == 0) { - cylinder_size = TRACK_SIZE * 2; - hole_size = TRACK_SIZE * remap; - } - else { - cylinder_size = TRACK_SIZE * remap; - hole_size = TRACK_SIZE * 2; - } - -/* Copy a platter. */ - - for (cyl = 0; cyl < 411; cyl++) { - -/* If stdout has been redirected, output the remapping information. */ - - if (debug) { - from_cyl = ftell (fin) / TRACK_SIZE; - to_cyl = ftell (fout) / TRACK_SIZE; - - if (platter == 1 && remap == 1) - printf ("track %i => %i\n", from_cyl, to_cyl); - else - printf ("track %i, %i => %i, %i\n", - from_cyl, from_cyl + 1, to_cyl, to_cyl + 1); - } - -/* Read a cylinder from the source location. */ - - record_size = fread (cylinder, 1, cylinder_size, fin); - -/* Swap the bytes. */ - - for (i = 0; i < record_size; i = i + 2) { - hold = cylinder [i]; - cylinder [i] = cylinder [i + 1]; - cylinder [i + 1] = hold; - } - -/* Write the cylinder to the target location. */ - - fwrite (cylinder, 1, record_size, fout); - -/* For platter-to-cylinder remapping, spread the tracks by seeking ahead in the - target file; this leaves room for the lower-platter tracks in between the - upper-platter tracks. For cylinder-to-platter remapping, condense the - cylinders by seeking ahead in the source file to the next track on the - current platter. */ - - if (reversed) - fseek (fout, hole_size, SEEK_CUR); - else - fseek (fin, hole_size, SEEK_CUR); - } - - -/* End of the current platter. For platter-to-cylinder remapping, reposition - the target file to the first "hole" left for the lower-platter tracks. For - cylinder-to-platter remapping, reposition the source file to access the first - lower-platter tracks. */ - - if (reversed) - fseek (fout, cylinder_size, SEEK_SET); - else - fseek (fin, cylinder_size, SEEK_SET); - } - - -/* Close the files. */ - - fclose (fin); - fclose (fout); - - -/* Delete the original file and replace it by the reversed file. */ - - if (unlink (name_in)) { - puts ("Error: cannot replace original file, which is unchanged."); - unlink (name_out); - return 1; - } - - else if (rename (name_out, name_in)) { - printf ("Error: cannot rename temporary file %s.\n", name_out); - return 1; - } - - -/* Return success. */ - - return 0; -} +/* Convert an HP disc image between SIMH and HPDrive formats. + + Copyright (c) 2012, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be used + in advertising or otherwise to promote the sale, use or other dealings in + this Software without prior written authorization from the author. + + + HPDrive is a free program written by Ansgar Kueckes that uses a PC and a GPIB + card to emulate a variety of vintage Hewlett-Packard disc and tape drives + that interface to computers via the HP-IB. It is available from: + + http://www.hp9845.net/9845/projects/hpdrive/ + + This program converts an HP disc image from SIMH to HPDrive format or + vice-versa. This permits interchanging images between the two programs. + + Usage: hpconvert + + SIMH writes and reads disc images in little-endian format, regardless of the + byte order of the host. In addition, SIMH accesses images for the the 7905 + and 7906 drives in platter order (i.e., all tracks on heads 0 and 1, followed + by all tracks on heads 2 and 3) to improve locality of access. It accesses + images for the 7920, 7925, and all CS/80 drives in cylinder order. + + HPDrive writes and reads images in big-endian format, regardless of the byte + order of the host, and accesses all images in cylinder order. + + This program swaps each pair of bytes in the disc image. In addition, if the + image is precisely the size of a 7905 or 7906 drive (15,151,104 or 20,201,472 + bytes, respectively), the access order is restructured from platter to + cylinder, or vice-versa. + + Note that SIMH does not currently create full-size disc images unless the + last sector on the drive is written. Therefore, it is recommended that new + images be initialized using the RTE FORMT or SWTCH programs to ensure that + the image files are of the correct size for this program and HPDrive. + + This program creates a scratch file to store the converted image. Only when + conversion is complete is the original file deleted and the scratch file + renamed to the original file's name. Running the program twice on the same + file will return the file to its original configuration. + + To decide the mode used in a 7905 or 7906 image, the program examines the OS + signature at the start of the file and compares it against a list of known + operating systems. If the OS signature is not recognized, the program + terminates without altering the file. Signature checking is not performed on + images other than those for the 7905 and 7906; those images are byte-swapped + unconditionally. + + Signatures for these operating systems and compatible 7905/06 drives are + recognized: + + - HP 1000 RTE-IVB: 7906H ICD + - HP 1000 RTE-6/VM: 7906H ICD + - HP 3000 MPE: 7905A/7906A MAC + + The signatures are contained in the first four words at the start of each + disc image. In hex representation, they are: + + - RTE-IVB ICD: 676D 06C0 776B 0B40 (LDA HHIGH ; CMA,CCE ; STA HRCNT ; ERB) + - RTE-6/VM ICD: 676D 06C0 776B 0B40 (LDA HHIGH ; CMA,CCE ; STA HRCNT ; ERB) + - MPE MAC: 5359 5354 454D 2044 ("SYSTEM D") + + These represent the start of the boot extension for RTE and the start of the + system disc label for MPE. The boot extension machine instructions are: + + RTE-IVB ICD + ----------- + + 62000 LDA EQU 062000B + 72000 STA EQU 072000B + 01200 O0 EQU HSTRT-1400B + + 02600 063555 HSTRT ABS LDA+HHIGH-O0 (word 1) + 02601 003300 CMA,CCE (word 2) + 02602 073553 ABS STA+HRCNT-O0 (word 3) + 02603 005500 ERB (word 4) + + 02753 000000 HRCNT NOP + + 02755 077377 NW#DS OCT 77377 + 02755 HHIGH EQU NW#DS + + RTE-6/VM ICD + ------------ + + 062000 LDA EQU 062000B + 072000 STA EQU 072000B + 000000R O0 EQU HSTRT-1400B + + 01400 063555 HSTRT ABS LDA+HHIGH-O0 (word 1) + 01401 003300 CMA,CCE (word 2) + 01402 073553 ABS STA+HRCNT-O0 (word 3) + 01403 005500 ERB (word 4) + + 01553 000000 HRCNT NOP + + 01555 077377 NW#DS OCT 77377 + 001555R HHIGH EQU NW#DS + + And the disc label is: + + MPE MAC and CS/80 + ----------------- + + 00000 051531 LABEL ASC 6,SYSTEM DISC (word 1) + 00001 051524 (word 2) + 00002 042515 (word 3) + 00003 020104 (word 4) + 00004 044523 + 00005 041440 + +*/ + + +#include +#include + + +/* MSVC does not provide "stdbool.h" or "unistd.h" */ + +#if defined (_MSC_VER) + typedef int bool; + const int false = 0; + #define isatty _isatty +#else + #include + #include +#endif + + +#define TRACK_SIZE ( 1 * 1 * 48 * 256) +#define CYLINDER_SIZE ( 1 * 2 * 48 * 256) +#define HP7905_SIZE (411 * 3 * 48 * 256) +#define HP7906_SIZE (411 * 4 * 48 * 256) + + +int main (int argc, + char **argv) + +{ + const char *format [] = { "HPDrive", "SIMH" }; + + const char *signatures [] = { "\x67\x6D\x06\xC0\x77\x6B\x0B\x40", /* RTE ICD */ + "SYSTEM D" }; /* MPE */ + + #define SIGNATURE_SIZE sizeof (signatures [0]) + + const int signature_count = sizeof (signatures) / SIGNATURE_SIZE; + + FILE *fin, *fout; + size_t file_size, record_size; + char *name_in, *name_out; + char sig_fwd [SIGNATURE_SIZE], sig_rev [SIGNATURE_SIZE]; + char hold, cylinder [CYLINDER_SIZE]; + bool identified = false, reversed = false, debug = false; + int cyl, from_cyl, to_cyl, remap; + int platter, cylinder_size, hole_size; + unsigned long i; + + +/* Read the disc image filename. */ + + if (argc != 2) { + puts ("\nHPConvert version 1.1"); + puts ("\nUsage: hpconvert "); + return 1; + } + + name_in = argv [1]; + +/* Open the source image file. */ + + fin = fopen (name_in, "rb"); + + if (!fin) { + printf ("Error: cannot open %s\n", name_in); + return 1; + } + +/* Get the size of the image. */ + + fseek (fin, 0, SEEK_END); + file_size = ftell (fin); + + +/* The blocks of a 7905 or 7906 image (as determined by the image file size) + will need to be rearranged. Set "remap" to the number of surfaces that must + be remapped. */ + + if (file_size == HP7905_SIZE) + remap = 1; + else if (file_size == HP7906_SIZE) + remap = 2; + else + remap = 0; + + +/* If the image is a 7905 or 7906, it must be remapped it for the target system. + To do that, check the OS signature to determine if it is in SIMH or HPDrive + format, and set the "reversed" flag if the signature bytes in the image are + reversed (this implies a platter-to-cylinder remapping of a 7905 or 7906 + image. */ + + if (remap) { + rewind (fin); + file_size = fread (sig_fwd, 1, SIGNATURE_SIZE, fin); + + for (i = 0; i < SIGNATURE_SIZE; i = i + 2) { + sig_rev [i] = sig_fwd [i + 1]; + sig_rev [i + 1] = sig_fwd [i]; + } + + for (i = 0; i < signature_count && identified == false; i++) { + reversed = strncmp (sig_rev, signatures [i], SIGNATURE_SIZE) == 0; + identified = reversed || strncmp (sig_fwd, signatures [i], SIGNATURE_SIZE) == 0; + } + +/* If the signature cannot be identified, then we do not know how to remap it, + so report the problem and exit. */ + + if (identified == false) { + printf ("Error: 790%i image OS signature not recognized.\n", remap + 4); + fclose (fin); + return 1; + } + } + + +/* Generate a temporary filename for the converted image. */ + + name_out = tmpnam (NULL); + + if (name_out == NULL) { + puts ("Error: cannot generate a temporary filename."); + fclose (fin); + return 1; + } + +/* Create the temporary target image. */ + + fout = fopen (name_out, "wb"); + + if (!fout) { + printf ("Error: cannot create %s\n", name_out); + fclose (fin); + return 1; + } + +/* Report the conversion that will be performed. */ + + if (remap) + printf ("Converting and remapping 790%i %s disc image to %s format.\n", + remap + 4, + format [reversed], + format [!reversed]); + else + puts ("Converting disc image."); + + +/* Enable debugging output if stdout has been redirected to a file. */ + + debug = isatty (fileno (stdout)) == 0; + + +/* Copy the source to the target image while swapping each byte pair. If the + disc image is for a 7905 or 7906, remap from platter to cylinder mode (or + vice-versa if the source image is not reversed). + + In a platter-mode image, the upper platter tracks appear in order before the + lower platter tracks. For the 7906, the cylinder-head order of the tracks is + 0-0, 0-1, 1-0, 1-1, ..., 410-0, 410-1, 0-2, 0-3, 1-2, 1-3, ..., 410-2, 410-3. + The 7905 order is the same, except that head 3 tracks are omitted. + + In a cylinder-mode image, all tracks appear in cylinder-head order, i.e., + 0-0, 0-1, 0-2, 0-3, 1-0, 1-1, ..., 410-2, 410-3, for the 7906. + + Remapping is performed in two passes, corresponding to the two platters. In + the first pass, the source tracks corresponding to the upper platter are + spread (platter-to-cylinder) or condensed (cylinder-to-platter) as they are + copied to the target file. In the second pass, the source tracks + corresponding to the lower platter are interleaved (platter-to-cylinder) or + appended (cylinder-to-platter) as they are copied to the target file. + + In either case, the bytes of each 16-bit word are swapped before copying. +*/ + + rewind (fin); + +/* If the image is not a 7905/06, simply swap each pair of bytes until EOF. */ + + if (!remap) + while (!feof (fin)) { + record_size = fread (cylinder, 1, CYLINDER_SIZE, fin); + + for (i = 0; i < record_size; i = i + 2) { + hold = cylinder [i]; + cylinder [i] = cylinder [i + 1]; + cylinder [i + 1] = hold; + } + + fwrite (cylinder, 1, record_size, fout); + } + +/* If the image is a 7905/06, remap the tracks and swap pairs of bytes. Because + we know the disc type, we know the number of platters and cylinders present + in the image. */ + + else + for (platter = 0; platter < 2; platter++) { + +/* Calculate the number of bytes per cylinder for the current platter. The + upper platter always has two tracks per cylinder. The lower platter has + either one (7905) or two (7906) tracks per cylinder. */ + + if (platter == 0) { + cylinder_size = TRACK_SIZE * 2; + hole_size = TRACK_SIZE * remap; + } + else { + cylinder_size = TRACK_SIZE * remap; + hole_size = TRACK_SIZE * 2; + } + +/* Copy a platter. */ + + for (cyl = 0; cyl < 411; cyl++) { + +/* If stdout has been redirected, output the remapping information. */ + + if (debug) { + from_cyl = ftell (fin) / TRACK_SIZE; + to_cyl = ftell (fout) / TRACK_SIZE; + + if (platter == 1 && remap == 1) + printf ("track %i => %i\n", from_cyl, to_cyl); + else + printf ("track %i, %i => %i, %i\n", + from_cyl, from_cyl + 1, to_cyl, to_cyl + 1); + } + +/* Read a cylinder from the source location. */ + + record_size = fread (cylinder, 1, cylinder_size, fin); + +/* Swap the bytes. */ + + for (i = 0; i < record_size; i = i + 2) { + hold = cylinder [i]; + cylinder [i] = cylinder [i + 1]; + cylinder [i + 1] = hold; + } + +/* Write the cylinder to the target location. */ + + fwrite (cylinder, 1, record_size, fout); + +/* For platter-to-cylinder remapping, spread the tracks by seeking ahead in the + target file; this leaves room for the lower-platter tracks in between the + upper-platter tracks. For cylinder-to-platter remapping, condense the + cylinders by seeking ahead in the source file to the next track on the + current platter. */ + + if (reversed) + fseek (fout, hole_size, SEEK_CUR); + else + fseek (fin, hole_size, SEEK_CUR); + } + + +/* End of the current platter. For platter-to-cylinder remapping, reposition + the target file to the first "hole" left for the lower-platter tracks. For + cylinder-to-platter remapping, reposition the source file to access the first + lower-platter tracks. */ + + if (reversed) + fseek (fout, cylinder_size, SEEK_SET); + else + fseek (fin, cylinder_size, SEEK_SET); + } + + +/* Close the files. */ + + fclose (fin); + fclose (fout); + + +/* Delete the original file and replace it by the reversed file. */ + + if (unlink (name_in)) { + puts ("Error: cannot replace original file, which is unchanged."); + unlink (name_out); + return 1; + } + + else if (rename (name_out, name_in)) { + printf ("Error: cannot rename temporary file %s.\n", name_out); + return 1; + } + + +/* Return success. */ + + return 0; +} diff --git a/converters/imd2dsk/imd2dsk.c b/converters/imd2dsk/imd2dsk.c index e3546d4..5e3146b 100644 --- a/converters/imd2dsk/imd2dsk.c +++ b/converters/imd2dsk/imd2dsk.c @@ -1,708 +1,708 @@ -/* - * IMD -> DSK (pure data) file converter - * - * Copyright (c) 2024-2025 Tony Lawrence - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the - * following conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names - * of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# define PACKED(...) \ - __pragma(pack(push, 1)) \ - __VA_ARGS__ \ - __pragma(pack(pop)) -#elif defined(__GNUC__) -# define PACKED(...) \ - __VA_ARGS__ \ - __attribute__((packed)) -#endif - - -typedef unsigned char uint1; -typedef signed char int1; -typedef unsigned short uint2; -typedef short int2; -typedef uint32_t uint4; -typedef int32_t int4; - - -/* IMD track header */ -PACKED(struct S_IMDTrkHdr { - uint1 mode; /* Track write mode (check only) */ - uint1 cyl; /* Physical cylinder # */ - uint1 head; /* Phys head(side) 0|1 and map flags*/ - uint1 nsect; /* Number of sectors to follow */ - uint1 ssize; /* Sector size or 0xFF for size tbl */ -}); - -#define MODE_MAX 5 /* Maximal valid track write mode */ - -#define SECTOR_CYL_MAP 0x80 /* Cylinder map for each sector */ -#define SECTOR_HEAD_MAP 0x40 /* Head map for each sector */ - -#define SECTOR_SIZE_TBL 0xFF /* Sector size table for each sector*/ -#define SECTOR_SIZE_MAX 8192 /* Maximal sector size supported */ - -#define IMD_HEADER_END '\x1A' /* Ctrl-Z (Text file EOF in MS-DOS) */ - - -static int verbose = 0; - -#define VERBOSE_SUMMARY 1 -#define VERBOSE_HEADER 2 -#define VERBOSE_TRACK 3 -#define VERBOSE_SECTOR 4 - - -/* Check IMD file header and return file pos where it ends */ -static long x_skip_header(FILE* fp, const char* file) -{ - int major, minor, ch; - struct tm tm, tmp; - - memset(&tm, 0, sizeof(tm)); - if (fscanf(fp, "IMD %d.%d: %d/%d/%d %d:%d:%d", - &major, &minor, - &tm.tm_mday, &tm.tm_mon, &tm.tm_year, - &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 8) { - goto out; - } - if (major <= 0 || minor < 0) - goto out; - if (tm.tm_mday < 1 || tm.tm_mday > 31 || - tm.tm_mon < 1 || tm.tm_mon > 12 || - tm.tm_year < 1900 || - tm.tm_hour < 0 || tm.tm_hour > 23 || - tm.tm_min < 0 || tm.tm_min > 59 || - tm.tm_sec < 0 || tm.tm_sec > 60) { - goto out; - } - tm.tm_mon--; - tm.tm_year -= 1900; - memcpy(&tmp, &tm, sizeof(tmp)); - if (mktime(&tmp) == (time_t)(-1L)) - goto out; - if (tm.tm_mday != tmp.tm_mday || - tm.tm_mon != tmp.tm_mon || - tm.tm_year != tmp.tm_year) { - /* NB: No check for time because - * the ranges are already okay yet - * DST can screw up the hours... */ - goto out; - } - - ch = fgetc(fp); - if (ch != IMD_HEADER_END) { - int/*bool*/ space = 1/*true*/; - if (!isspace(ch)) - goto out; - if (verbose >= VERBOSE_HEADER) { - char timebuf[80]; - strftime(timebuf, sizeof(timebuf), "%d/%m/%Y %T", &tm); - printf("IMD %d.%d: %s\n", major, minor, timebuf); - } - for (;;) { - ch = fgetc(fp); - if (ch == IMD_HEADER_END) - break; - if (isspace(ch)) { - if (space) - continue; - } else { - if (!isprint(ch)) - goto out; - space = 0/*false*/; - } - if (verbose >= VERBOSE_HEADER) - putchar(ch); - } - if (!space && verbose >= VERBOSE_HEADER) - putchar('\n'); - } - - return ftell(fp); - -out: - fprintf(stderr, "%s: Invalid IMD file header\n", file); - return 0; -} - - -inline -static int/*bool*/ x_check_mode(uint1 mode) -{ - return mode > MODE_MAX ? 0/*false*/ : 1/*true*/; -} - - -inline -static uint2 x_sector_size(uint1 ssize) -{ - return ssize > 6 ? SECTOR_SIZE_MAX + 1 : 1 << (7 + ssize); -} - - -static int/*bool*/ x_skip_sector(FILE* fp, uint2 ssize) -{ - int ch = fgetc(fp); - if (ch == EOF) - return 0/*false*/; - if (ch < 0 || ch > 8) - return 0/*false*/; - if (!ch) - return 1/*true*/; - return fseek(fp, ch & 1 ? ssize : 1, SEEK_CUR) == 0 ? 1/*T*/ : 0/*F*/; -} - - -/* Disk parameters */ -static int cyls = 0; -static int heads = 0; -static int sectors = 0; -static int sector_size = 0; -static uint1 sector_filler = 0; -static uint1 sector[SECTOR_SIZE_MAX]; - -#define DA(C, H, S) (((C) * heads + (H)) * sectors + (S) - one_based) -#define DA_OFFSET(da) ((da) * sector_size) - - -/* Sector numbering */ -static int no_maps = 0; /* =1 no cyl/head maps; =2 no sector maps, either */ -static int max_sect = 0; -static int zero_sect = 0; -static int one_based = 1; - - -/* Scan the file, set up disk params and return the number of tracks found */ -static int x_scan_tracks(FILE* fp, const char* file) -{ - const char* problem = 0; - char buf[80]; - int track; - - for (track = 0; ; ++track) { - struct S_IMDTrkHdr hdr; - uint2* stable; - uint2 ssize; - long skip; - int n; - - if (fread(&hdr, sizeof(hdr), 1, fp) != 1) { - if (feof(fp) && track) - break; - problem = "File read error"; - goto out; - } - if (!x_check_mode(hdr.mode)) { - sprintf(buf, "Unrecognized track write mode %u", hdr.mode); - problem = buf; - goto out; - } - ssize = hdr.ssize; - if (ssize != SECTOR_SIZE_TBL) { - ssize = x_sector_size(hdr.ssize); - assert(ssize); - if (ssize & (ssize - 1)) { - sprintf(buf, "Invalid sector size %hu", ssize); - problem = buf; - goto out; - } - assert(ssize <= SECTOR_SIZE_MAX); - if (!sector_size) - sector_size = ssize; - else if (sector_size != ssize) { - sprintf(buf, "Sector size change %d -> %hu", sector_size, ssize); - problem = buf; - goto out; - } - } - n = hdr.head & ~(SECTOR_CYL_MAP | SECTOR_HEAD_MAP); - if (n & ~1) { - sprintf(buf, "Invalid head number %d", n); - problem = buf; - goto out; - } - if (heads <= n) - heads++; - if (cyls <= hdr.cyl) - cyls = hdr.cyl + 1; - if (!hdr.nsect) - continue; - if (sectors < hdr.nsect) - sectors = hdr.nsect; - if (no_maps < 2) { - uint1 smap[256]; - if (fread(smap, hdr.nsect, 1, fp) != 1) { - problem = "Cannot read sector map"; - goto out; - } - for (n = 0; n < hdr.nsect; ++n) { - if (!smap[n]) { - ++zero_sect; - continue; - } - if (max_sect < smap[n]) - max_sect = smap[n]; - } - skip = 0; - } else - skip = hdr.nsect; - if (hdr.head & SECTOR_CYL_MAP) - skip += hdr.nsect; - if (hdr.head & SECTOR_HEAD_MAP) - skip += hdr.nsect; - if (skip && fseek(fp, skip, SEEK_CUR) != 0) { - problem = "File positioning error"; - goto out; - } - if (ssize == SECTOR_SIZE_TBL) { - if (!(stable = (uint2*) malloc(sizeof(*stable) * hdr.nsect))) { - problem = "Cannot allocate for sector size table"; - goto out; - } - if (fread(stable, sizeof(*stable), hdr.nsect, fp) != hdr.nsect) { - problem = "Cannot read sector size table"; - goto out; - } - } else - stable = 0; - for (n = 0; n < hdr.nsect; ++n) { - if (stable) { - ssize = stable[n]; - if (!ssize || (ssize & (ssize - 1)) || ssize > SECTOR_SIZE_MAX) { - sprintf(buf, "Invalid sector size %hu in sector %d", - ssize, n + 1); - problem = buf; - break; - } - if (!sector_size) - sector_size = ssize; - else if (sector_size != ssize) { - sprintf(buf, "Sector size change %d -> %hu in sector %d", - sector_size, ssize, n + 1); - problem = buf; - break; - } - } else - assert(ssize && !(ssize & (ssize - 1)) && ssize <= SECTOR_SIZE_MAX); - if (!x_skip_sector(fp, ssize)) { - sprintf(buf, "Error skipping sector %d (size %hu)", n + 1, ssize); - problem = buf; - break; - } - } - if (stable) - free(stable); - if (n < hdr.nsect) { - assert(problem); - goto out; - } - } - - return track; - -out: - assert(problem); - fprintf(stderr, "%s (scanning track data %d): %s\n", file, track, problem); - return 0/*error*/; -} - - -/* Summary */ -static int n_restored = 0; -static int n_compressed = 0; - -/* Storage map */ -static uint1* map = 0; - - -static int/*bool*/ x_extract_sector(int C, int H, int S, int track, - FILE* in, const char* infile, - FILE* out, const char* outfile) -{ - int/*bool*/ duplicate = 0/*false*/; - const char* problem = 0; - const char* file = 0; - int ch; - - /* Disk Address */ - long da = DA(C, H, S); - assert(0 <= da && da < cyls * heads * sectors); - - if (map[da >> 3] & (1 << (da & 7))) { - fprintf(stderr, "%s: WARNING -- Duplicate sector %d/%d/%d in track data %d\n", - infile, C, H, S, track); - duplicate = 1/*true*/; - } else - map[da >> 3] |= 1 << (da & 7); - - ch = fgetc(in); - if (ch == EOF) { - problem = "File read error"; - file = infile; - goto out; - } - - switch (ch) { - case 0: - problem = "Sector data unavailable"; - break; - case 2: case 1: - /* normal sector data */ - assert(!problem); - break; - case 4: case 3: - problem = "Deleted data"; - break; - case 6: case 5: - problem = "Sector data with error"; - break; - case 8: case 7: - problem = "Deleted data with error"; - break; - default: - /* NB: this should never happen per x_skip_sector() */ - abort(); - } - if (problem) - fprintf(stderr, "%s: WARNING -- %d/%d/%d: %s\n", infile, C, H, S, problem); - - assert(0 < sector_size && sector_size <= (int) sizeof(sector)); - if (ch & 1) { - if (fread(sector, sector_size, 1, in) != 1) { - problem = "Sector data read error"; - file = infile; - goto out; - } - } else { - if (ch) { - ch = fgetc(in); - if (ch == EOF) { - problem = "Compressed sector data read error"; - file = infile; - goto out; - } - } else/* technically it's still a "compressed" sector */ - ch = sector_filler; - memset(sector, ch, sector_size); - if (!duplicate) - ++n_compressed; - } - - if (fseek(out, DA_OFFSET(da), SEEK_SET) != 0) { - problem = "File positioning error"; - file = outfile; - goto out; - } - if (fwrite(sector, sector_size, 1, out) != 1) { - problem = "File write error"; - file = outfile; - goto out; - } - - if (!duplicate) - ++n_restored; - return 1/*true*/; - -out: - assert(file && problem); - fprintf(stderr, "%s (extracting track data %d for %d/%d/%d): %s\n", - file, track, C, H, S, problem); - return 0/*false*/; -} - - -static int/*bool*/ x_extract_track(int track, - FILE* in, const char* infile, - FILE* out, const char* outfile) -{ - static uint1 cmap[256]; - static uint1 hmap[256]; - static uint1 smap[256]; - const char* problem = 0; - struct S_IMDTrkHdr hdr; - char buf[80]; - long skip; - int n; - - if (verbose >= VERBOSE_TRACK) - fprintf(stderr, "Track data %d\n", track); - - if (fread(&hdr, sizeof(hdr), 1, in) != 1) { - problem = "File read error"; - goto out; - } - assert(x_check_mode(hdr.mode)); - if (!hdr.nsect) - return 1/*true*/; - - if (no_maps < 2) { - if (fread(smap, hdr.nsect, 1, in) != 1) { - problem = "Cannot read sector map"; - goto out; - } - skip = 0; - } else - skip = hdr.nsect; - if (no_maps) { - if (hdr.head & SECTOR_CYL_MAP) - skip += hdr.nsect; - if (hdr.head & SECTOR_HEAD_MAP) - skip += hdr.nsect; - hdr.head &= ~(SECTOR_CYL_MAP | SECTOR_HEAD_MAP); - } else { - if ((hdr.head & SECTOR_CYL_MAP) - && fread(cmap, hdr.nsect, 1, in) != 1) { - problem = "Cannot read cylinder map"; - goto out; - } - if ((hdr.head & SECTOR_HEAD_MAP) - && fread(hmap, hdr.nsect, 1, in) != 1) { - problem = "Cannot read head map"; - goto out; - } - } - if (hdr.ssize == SECTOR_SIZE_TBL) - skip += sizeof(uint2) * hdr.nsect; - if (skip && fseek(in, skip, SEEK_CUR) != 0) { - problem = "File positioning error"; - goto out; - } - - for (n = 0; n < hdr.nsect; ++n) { - int C = hdr.head & SECTOR_CYL_MAP ? cmap[n] : hdr.cyl; - int H = hdr.head & SECTOR_HEAD_MAP ? hmap[n] : hdr.head & 1; - int S = no_maps < 2 ? smap[n] : n + 1; - if (verbose >= VERBOSE_SECTOR) - fprintf(stderr, "Extracting: %d/%d/%d\n", C, H, S); - if (C < 0 || C >= cyls) { - sprintf(buf, "Cylinder %d out of range [0..%d]", C, cyls - 1); - problem = buf; - goto out; - } - if (H < 0 || H >= heads) { - sprintf(buf, "Head %d out of range [0..%d]", H, heads - 1); - problem = buf; - goto out; - } - if (S < one_based || S >= sectors + one_based) { - sprintf(buf, "Sector %d out of range [%d..%d]", S, - one_based, sectors - !one_based); - problem = buf; - goto out; - } - if (!x_extract_sector(C, H, S, track, in, infile, out, outfile)) - return 0/*false*/; - } - - return 1/*true*/; - -out: - assert(problem); - fprintf(stderr, "%s (extracting track data %d): %s\n", infile, track, problem); - return 0/*false*/; -} - - -#ifdef __GNUC__ -__attribute__((noreturn)) -#endif -static void usage(const char* prog) -{ - fprintf(stderr, "%s [-i] [-i] [-c val] [-v...] infile outfile\n\n", prog); - fprintf(stderr, "-i = Ignore cylinder / head maps; second -i: Ignore sector maps, too\n" - "-c = Use 'val' as byte-filler in missing sectors (default: 0)\n" - "-v = Increase verbosity with each occurrence\n"); - exit(2); -} - - -#if defined(__CYGWIN__) -# define _stricmp strcasecmp -#elif !defined(_MSC_VER) -# define _stricmp strcmp -#else -# define strcmp _strcmp -#endif - - -int main(int argc, char* argv[]) -{ - const char* infile; - const char* outfile; - int p, q, tracks; - FILE* in; - FILE* out; - long pos; - - p = 1; - if (argc > 1) { - do { - if (strcmp(argv[p], "-v") == 0) { - ++verbose; - continue; - } - if (strcmp(argv[p], "-c") == 0) { - char* end; - errno = 0; - if (sector_filler || !argv[++p] || - !(sector_filler = strtol(argv[p], &end, 0)) || errno || *end) { - usage(argv[0]); - } - continue; - } - if (strcmp(argv[p], "-i") != 0) - break; - if (no_maps > 1) - usage(argv[0]); - ++no_maps; - } while (argv[++p]); - } - if (p < argc && strcmp(argv[p], "--") == 0) - ++p; - if (argc < 3 - || !(infile = argv[p++]) || !(outfile = argv[p++]) || argv[p] - || _stricmp(infile, outfile) == 0) { - usage(argv[0]); - } - - if (!(in = fopen(infile, "rb"))) { - perror(infile); - return EXIT_FAILURE; - } - - if (!(pos = x_skip_header(in, infile))) - return EXIT_FAILURE; - if (pos == -1L) { - fprintf(stderr, "%s: File positioning error", infile); - return EXIT_FAILURE; - } - - if (!(tracks = x_scan_tracks(in, infile))) - return EXIT_FAILURE; - assert(heads <= 2 && sectors <= 255 && sector_size <= SECTOR_SIZE_MAX); - - if (heads <= 0 || sectors <= 0 || sector_size <= 0 - || cyls < (tracks + heads - 1) / heads || 255 < cyls) { - fprintf(stderr, "%s: Failed to determine disk geometry, sorry\n", infile); - return EXIT_FAILURE; - } - - if (zero_sect > (tracks >> 2) && max_sect < sectors) - one_based = 0; - if (verbose >= VERBOSE_SUMMARY) { - fprintf(stderr, "%s: CHS = %d/%d/%d; Sector size = %d%s\n", - infile, cyls, heads, sectors, sector_size, - one_based ? "" : "; 0-based sector numbering"); - } - - if (fseek(in, pos, SEEK_SET) != 0) { - perror(infile); - return EXIT_FAILURE; - } - - q = cyls * heads * sectors; - assert(0 < q && q <= 255 * 2 * 255 /*130050*/); - if (!(map = (uint1*) calloc((q + 7) / 8, sizeof(*map)))) { - perror(outfile); - return EXIT_FAILURE; - } - - if (!(out = fopen(outfile, "wb"))) { - perror(outfile); - return EXIT_FAILURE; - } - - for (p = 0; p < tracks; ++p) { - if (!x_extract_track(p, in, infile, out, outfile)) - return EXIT_FAILURE; - } - - if (sector_filler) - memset(sector, sector_filler, sector_size); - for (p = 0; p < q; p += 8) { - int k, n = p >> 3; - if (map[n] == (1 << 8) - 1) - continue; - for (k = 0; k < 8; ++k) { - int C, H, S = p | k; - if (S >= q) - break; - if (map[n] & (1 << k)) - continue; - H = S / sectors; - S %= sectors; - C = H / heads; - H %= heads; - S += one_based; - fprintf(stderr, "%s: WARNING -- Sector not stored: %d/%d/%d\n", - outfile, C, H, S); - if (sector_filler) { - long da = DA(C, H, S); - assert(0 <= da && da < cyls * heads * sectors); - if (fseek(out, DA_OFFSET(da), SEEK_SET) != 0 - || fwrite(sector, sector_size, 1, out) != 1) { - fprintf(stderr, "%s: Error writing sector filler\n", outfile); - } - } - } - } - - free(map); - - if (fclose(out) != 0) { - fprintf(stderr, "%s: Closing error\n", outfile); - return EXIT_FAILURE; - } - if (verbose >= VERBOSE_SUMMARY) { - printf("%s: Total sectors restored: %d", outfile, n_restored); - if (n_compressed) - printf(", of those compressed: %d\n", n_compressed); - else - putchar('\n'); - } - fclose(in); - return EXIT_SUCCESS; -} +/* + * IMD -> DSK (pure data) file converter + * + * Copyright (c) 2024-2025 Tony Lawrence + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the + * following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names + * of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# define PACKED(...) \ + __pragma(pack(push, 1)) \ + __VA_ARGS__ \ + __pragma(pack(pop)) +#elif defined(__GNUC__) +# define PACKED(...) \ + __VA_ARGS__ \ + __attribute__((packed)) +#endif + + +typedef unsigned char uint1; +typedef signed char int1; +typedef unsigned short uint2; +typedef short int2; +typedef uint32_t uint4; +typedef int32_t int4; + + +/* IMD track header */ +PACKED(struct S_IMDTrkHdr { + uint1 mode; /* Track write mode (check only) */ + uint1 cyl; /* Physical cylinder # */ + uint1 head; /* Phys head(side) 0|1 and map flags*/ + uint1 nsect; /* Number of sectors to follow */ + uint1 ssize; /* Sector size or 0xFF for size tbl */ +}); + +#define MODE_MAX 5 /* Maximal valid track write mode */ + +#define SECTOR_CYL_MAP 0x80 /* Cylinder map for each sector */ +#define SECTOR_HEAD_MAP 0x40 /* Head map for each sector */ + +#define SECTOR_SIZE_TBL 0xFF /* Sector size table for each sector*/ +#define SECTOR_SIZE_MAX 8192 /* Maximal sector size supported */ + +#define IMD_HEADER_END '\x1A' /* Ctrl-Z (Text file EOF in MS-DOS) */ + + +static int verbose = 0; + +#define VERBOSE_SUMMARY 1 +#define VERBOSE_HEADER 2 +#define VERBOSE_TRACK 3 +#define VERBOSE_SECTOR 4 + + +/* Check IMD file header and return file pos where it ends */ +static long x_skip_header(FILE* fp, const char* file) +{ + int major, minor, ch; + struct tm tm, tmp; + + memset(&tm, 0, sizeof(tm)); + if (fscanf(fp, "IMD %d.%d: %d/%d/%d %d:%d:%d", + &major, &minor, + &tm.tm_mday, &tm.tm_mon, &tm.tm_year, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 8) { + goto out; + } + if (major <= 0 || minor < 0) + goto out; + if (tm.tm_mday < 1 || tm.tm_mday > 31 || + tm.tm_mon < 1 || tm.tm_mon > 12 || + tm.tm_year < 1900 || + tm.tm_hour < 0 || tm.tm_hour > 23 || + tm.tm_min < 0 || tm.tm_min > 59 || + tm.tm_sec < 0 || tm.tm_sec > 60) { + goto out; + } + tm.tm_mon--; + tm.tm_year -= 1900; + memcpy(&tmp, &tm, sizeof(tmp)); + if (mktime(&tmp) == (time_t)(-1L)) + goto out; + if (tm.tm_mday != tmp.tm_mday || + tm.tm_mon != tmp.tm_mon || + tm.tm_year != tmp.tm_year) { + /* NB: No check for time because + * the ranges are already okay yet + * DST can screw up the hours... */ + goto out; + } + + ch = fgetc(fp); + if (ch != IMD_HEADER_END) { + int/*bool*/ space = 1/*true*/; + if (!isspace(ch)) + goto out; + if (verbose >= VERBOSE_HEADER) { + char timebuf[80]; + strftime(timebuf, sizeof(timebuf), "%d/%m/%Y %T", &tm); + printf("IMD %d.%d: %s\n", major, minor, timebuf); + } + for (;;) { + ch = fgetc(fp); + if (ch == IMD_HEADER_END) + break; + if (isspace(ch)) { + if (space) + continue; + } else { + if (!isprint(ch)) + goto out; + space = 0/*false*/; + } + if (verbose >= VERBOSE_HEADER) + putchar(ch); + } + if (!space && verbose >= VERBOSE_HEADER) + putchar('\n'); + } + + return ftell(fp); + +out: + fprintf(stderr, "%s: Invalid IMD file header\n", file); + return 0; +} + + +inline +static int/*bool*/ x_check_mode(uint1 mode) +{ + return mode > MODE_MAX ? 0/*false*/ : 1/*true*/; +} + + +inline +static uint2 x_sector_size(uint1 ssize) +{ + return ssize > 6 ? SECTOR_SIZE_MAX + 1 : 1 << (7 + ssize); +} + + +static int/*bool*/ x_skip_sector(FILE* fp, uint2 ssize) +{ + int ch = fgetc(fp); + if (ch == EOF) + return 0/*false*/; + if (ch < 0 || ch > 8) + return 0/*false*/; + if (!ch) + return 1/*true*/; + return fseek(fp, ch & 1 ? ssize : 1, SEEK_CUR) == 0 ? 1/*T*/ : 0/*F*/; +} + + +/* Disk parameters */ +static int cyls = 0; +static int heads = 0; +static int sectors = 0; +static int sector_size = 0; +static uint1 sector_filler = 0; +static uint1 sector[SECTOR_SIZE_MAX]; + +#define DA(C, H, S) (((C) * heads + (H)) * sectors + (S) - one_based) +#define DA_OFFSET(da) ((da) * sector_size) + + +/* Sector numbering */ +static int no_maps = 0; /* =1 no cyl/head maps; =2 no sector maps, either */ +static int max_sect = 0; +static int zero_sect = 0; +static int one_based = 1; + + +/* Scan the file, set up disk params and return the number of tracks found */ +static int x_scan_tracks(FILE* fp, const char* file) +{ + const char* problem = 0; + char buf[80]; + int track; + + for (track = 0; ; ++track) { + struct S_IMDTrkHdr hdr; + uint2* stable; + uint2 ssize; + long skip; + int n; + + if (fread(&hdr, sizeof(hdr), 1, fp) != 1) { + if (feof(fp) && track) + break; + problem = "File read error"; + goto out; + } + if (!x_check_mode(hdr.mode)) { + sprintf(buf, "Unrecognized track write mode %u", hdr.mode); + problem = buf; + goto out; + } + ssize = hdr.ssize; + if (ssize != SECTOR_SIZE_TBL) { + ssize = x_sector_size(hdr.ssize); + assert(ssize); + if (ssize & (ssize - 1)) { + sprintf(buf, "Invalid sector size %hu", ssize); + problem = buf; + goto out; + } + assert(ssize <= SECTOR_SIZE_MAX); + if (!sector_size) + sector_size = ssize; + else if (sector_size != ssize) { + sprintf(buf, "Sector size change %d -> %hu", sector_size, ssize); + problem = buf; + goto out; + } + } + n = hdr.head & ~(SECTOR_CYL_MAP | SECTOR_HEAD_MAP); + if (n & ~1) { + sprintf(buf, "Invalid head number %d", n); + problem = buf; + goto out; + } + if (heads <= n) + heads++; + if (cyls <= hdr.cyl) + cyls = hdr.cyl + 1; + if (!hdr.nsect) + continue; + if (sectors < hdr.nsect) + sectors = hdr.nsect; + if (no_maps < 2) { + uint1 smap[256]; + if (fread(smap, hdr.nsect, 1, fp) != 1) { + problem = "Cannot read sector map"; + goto out; + } + for (n = 0; n < hdr.nsect; ++n) { + if (!smap[n]) { + ++zero_sect; + continue; + } + if (max_sect < smap[n]) + max_sect = smap[n]; + } + skip = 0; + } else + skip = hdr.nsect; + if (hdr.head & SECTOR_CYL_MAP) + skip += hdr.nsect; + if (hdr.head & SECTOR_HEAD_MAP) + skip += hdr.nsect; + if (skip && fseek(fp, skip, SEEK_CUR) != 0) { + problem = "File positioning error"; + goto out; + } + if (ssize == SECTOR_SIZE_TBL) { + if (!(stable = (uint2*) malloc(sizeof(*stable) * hdr.nsect))) { + problem = "Cannot allocate for sector size table"; + goto out; + } + if (fread(stable, sizeof(*stable), hdr.nsect, fp) != hdr.nsect) { + problem = "Cannot read sector size table"; + goto out; + } + } else + stable = 0; + for (n = 0; n < hdr.nsect; ++n) { + if (stable) { + ssize = stable[n]; + if (!ssize || (ssize & (ssize - 1)) || ssize > SECTOR_SIZE_MAX) { + sprintf(buf, "Invalid sector size %hu in sector %d", + ssize, n + 1); + problem = buf; + break; + } + if (!sector_size) + sector_size = ssize; + else if (sector_size != ssize) { + sprintf(buf, "Sector size change %d -> %hu in sector %d", + sector_size, ssize, n + 1); + problem = buf; + break; + } + } else + assert(ssize && !(ssize & (ssize - 1)) && ssize <= SECTOR_SIZE_MAX); + if (!x_skip_sector(fp, ssize)) { + sprintf(buf, "Error skipping sector %d (size %hu)", n + 1, ssize); + problem = buf; + break; + } + } + if (stable) + free(stable); + if (n < hdr.nsect) { + assert(problem); + goto out; + } + } + + return track; + +out: + assert(problem); + fprintf(stderr, "%s (scanning track data %d): %s\n", file, track, problem); + return 0/*error*/; +} + + +/* Summary */ +static int n_restored = 0; +static int n_compressed = 0; + +/* Storage map */ +static uint1* map = 0; + + +static int/*bool*/ x_extract_sector(int C, int H, int S, int track, + FILE* in, const char* infile, + FILE* out, const char* outfile) +{ + int/*bool*/ duplicate = 0/*false*/; + const char* problem = 0; + const char* file = 0; + int ch; + + /* Disk Address */ + long da = DA(C, H, S); + assert(0 <= da && da < cyls * heads * sectors); + + if (map[da >> 3] & (1 << (da & 7))) { + fprintf(stderr, "%s: WARNING -- Duplicate sector %d/%d/%d in track data %d\n", + infile, C, H, S, track); + duplicate = 1/*true*/; + } else + map[da >> 3] |= 1 << (da & 7); + + ch = fgetc(in); + if (ch == EOF) { + problem = "File read error"; + file = infile; + goto out; + } + + switch (ch) { + case 0: + problem = "Sector data unavailable"; + break; + case 2: case 1: + /* normal sector data */ + assert(!problem); + break; + case 4: case 3: + problem = "Deleted data"; + break; + case 6: case 5: + problem = "Sector data with error"; + break; + case 8: case 7: + problem = "Deleted data with error"; + break; + default: + /* NB: this should never happen per x_skip_sector() */ + abort(); + } + if (problem) + fprintf(stderr, "%s: WARNING -- %d/%d/%d: %s\n", infile, C, H, S, problem); + + assert(0 < sector_size && sector_size <= (int) sizeof(sector)); + if (ch & 1) { + if (fread(sector, sector_size, 1, in) != 1) { + problem = "Sector data read error"; + file = infile; + goto out; + } + } else { + if (ch) { + ch = fgetc(in); + if (ch == EOF) { + problem = "Compressed sector data read error"; + file = infile; + goto out; + } + } else/* technically it's still a "compressed" sector */ + ch = sector_filler; + memset(sector, ch, sector_size); + if (!duplicate) + ++n_compressed; + } + + if (fseek(out, DA_OFFSET(da), SEEK_SET) != 0) { + problem = "File positioning error"; + file = outfile; + goto out; + } + if (fwrite(sector, sector_size, 1, out) != 1) { + problem = "File write error"; + file = outfile; + goto out; + } + + if (!duplicate) + ++n_restored; + return 1/*true*/; + +out: + assert(file && problem); + fprintf(stderr, "%s (extracting track data %d for %d/%d/%d): %s\n", + file, track, C, H, S, problem); + return 0/*false*/; +} + + +static int/*bool*/ x_extract_track(int track, + FILE* in, const char* infile, + FILE* out, const char* outfile) +{ + static uint1 cmap[256]; + static uint1 hmap[256]; + static uint1 smap[256]; + const char* problem = 0; + struct S_IMDTrkHdr hdr; + char buf[80]; + long skip; + int n; + + if (verbose >= VERBOSE_TRACK) + fprintf(stderr, "Track data %d\n", track); + + if (fread(&hdr, sizeof(hdr), 1, in) != 1) { + problem = "File read error"; + goto out; + } + assert(x_check_mode(hdr.mode)); + if (!hdr.nsect) + return 1/*true*/; + + if (no_maps < 2) { + if (fread(smap, hdr.nsect, 1, in) != 1) { + problem = "Cannot read sector map"; + goto out; + } + skip = 0; + } else + skip = hdr.nsect; + if (no_maps) { + if (hdr.head & SECTOR_CYL_MAP) + skip += hdr.nsect; + if (hdr.head & SECTOR_HEAD_MAP) + skip += hdr.nsect; + hdr.head &= ~(SECTOR_CYL_MAP | SECTOR_HEAD_MAP); + } else { + if ((hdr.head & SECTOR_CYL_MAP) + && fread(cmap, hdr.nsect, 1, in) != 1) { + problem = "Cannot read cylinder map"; + goto out; + } + if ((hdr.head & SECTOR_HEAD_MAP) + && fread(hmap, hdr.nsect, 1, in) != 1) { + problem = "Cannot read head map"; + goto out; + } + } + if (hdr.ssize == SECTOR_SIZE_TBL) + skip += sizeof(uint2) * hdr.nsect; + if (skip && fseek(in, skip, SEEK_CUR) != 0) { + problem = "File positioning error"; + goto out; + } + + for (n = 0; n < hdr.nsect; ++n) { + int C = hdr.head & SECTOR_CYL_MAP ? cmap[n] : hdr.cyl; + int H = hdr.head & SECTOR_HEAD_MAP ? hmap[n] : hdr.head & 1; + int S = no_maps < 2 ? smap[n] : n + 1; + if (verbose >= VERBOSE_SECTOR) + fprintf(stderr, "Extracting: %d/%d/%d\n", C, H, S); + if (C < 0 || C >= cyls) { + sprintf(buf, "Cylinder %d out of range [0..%d]", C, cyls - 1); + problem = buf; + goto out; + } + if (H < 0 || H >= heads) { + sprintf(buf, "Head %d out of range [0..%d]", H, heads - 1); + problem = buf; + goto out; + } + if (S < one_based || S >= sectors + one_based) { + sprintf(buf, "Sector %d out of range [%d..%d]", S, + one_based, sectors - !one_based); + problem = buf; + goto out; + } + if (!x_extract_sector(C, H, S, track, in, infile, out, outfile)) + return 0/*false*/; + } + + return 1/*true*/; + +out: + assert(problem); + fprintf(stderr, "%s (extracting track data %d): %s\n", infile, track, problem); + return 0/*false*/; +} + + +#ifdef __GNUC__ +__attribute__((noreturn)) +#endif +static void usage(const char* prog) +{ + fprintf(stderr, "%s [-i] [-i] [-c val] [-v...] infile outfile\n\n", prog); + fprintf(stderr, "-i = Ignore cylinder / head maps; second -i: Ignore sector maps, too\n" + "-c = Use 'val' as byte-filler in missing sectors (default: 0)\n" + "-v = Increase verbosity with each occurrence\n"); + exit(2); +} + + +#if defined(__CYGWIN__) +# define _stricmp strcasecmp +#elif !defined(_MSC_VER) +# define _stricmp strcmp +#else +# define strcmp _strcmp +#endif + + +int main(int argc, char* argv[]) +{ + const char* infile; + const char* outfile; + int p, q, tracks; + FILE* in; + FILE* out; + long pos; + + p = 1; + if (argc > 1) { + do { + if (strcmp(argv[p], "-v") == 0) { + ++verbose; + continue; + } + if (strcmp(argv[p], "-c") == 0) { + char* end; + errno = 0; + if (sector_filler || !argv[++p] || + !(sector_filler = strtol(argv[p], &end, 0)) || errno || *end) { + usage(argv[0]); + } + continue; + } + if (strcmp(argv[p], "-i") != 0) + break; + if (no_maps > 1) + usage(argv[0]); + ++no_maps; + } while (argv[++p]); + } + if (p < argc && strcmp(argv[p], "--") == 0) + ++p; + if (argc < 3 + || !(infile = argv[p++]) || !(outfile = argv[p++]) || argv[p] + || _stricmp(infile, outfile) == 0) { + usage(argv[0]); + } + + if (!(in = fopen(infile, "rb"))) { + perror(infile); + return EXIT_FAILURE; + } + + if (!(pos = x_skip_header(in, infile))) + return EXIT_FAILURE; + if (pos == -1L) { + fprintf(stderr, "%s: File positioning error", infile); + return EXIT_FAILURE; + } + + if (!(tracks = x_scan_tracks(in, infile))) + return EXIT_FAILURE; + assert(heads <= 2 && sectors <= 255 && sector_size <= SECTOR_SIZE_MAX); + + if (heads <= 0 || sectors <= 0 || sector_size <= 0 + || cyls < (tracks + heads - 1) / heads || 255 < cyls) { + fprintf(stderr, "%s: Failed to determine disk geometry, sorry\n", infile); + return EXIT_FAILURE; + } + + if (zero_sect > (tracks >> 2) && max_sect < sectors) + one_based = 0; + if (verbose >= VERBOSE_SUMMARY) { + fprintf(stderr, "%s: CHS = %d/%d/%d; Sector size = %d%s\n", + infile, cyls, heads, sectors, sector_size, + one_based ? "" : "; 0-based sector numbering"); + } + + if (fseek(in, pos, SEEK_SET) != 0) { + perror(infile); + return EXIT_FAILURE; + } + + q = cyls * heads * sectors; + assert(0 < q && q <= 255 * 2 * 255 /*130050*/); + if (!(map = (uint1*) calloc((q + 7) / 8, sizeof(*map)))) { + perror(outfile); + return EXIT_FAILURE; + } + + if (!(out = fopen(outfile, "wb"))) { + perror(outfile); + return EXIT_FAILURE; + } + + for (p = 0; p < tracks; ++p) { + if (!x_extract_track(p, in, infile, out, outfile)) + return EXIT_FAILURE; + } + + if (sector_filler) + memset(sector, sector_filler, sector_size); + for (p = 0; p < q; p += 8) { + int k, n = p >> 3; + if (map[n] == (1 << 8) - 1) + continue; + for (k = 0; k < 8; ++k) { + int C, H, S = p | k; + if (S >= q) + break; + if (map[n] & (1 << k)) + continue; + H = S / sectors; + S %= sectors; + C = H / heads; + H %= heads; + S += one_based; + fprintf(stderr, "%s: WARNING -- Sector not stored: %d/%d/%d\n", + outfile, C, H, S); + if (sector_filler) { + long da = DA(C, H, S); + assert(0 <= da && da < cyls * heads * sectors); + if (fseek(out, DA_OFFSET(da), SEEK_SET) != 0 + || fwrite(sector, sector_size, 1, out) != 1) { + fprintf(stderr, "%s: Error writing sector filler\n", outfile); + } + } + } + } + + free(map); + + if (fclose(out) != 0) { + fprintf(stderr, "%s: Closing error\n", outfile); + return EXIT_FAILURE; + } + if (verbose >= VERBOSE_SUMMARY) { + printf("%s: Total sectors restored: %d", outfile, n_restored); + if (n_compressed) + printf(", of those compressed: %d\n", n_compressed); + else + putchar('\n'); + } + fclose(in); + return EXIT_SUCCESS; +} diff --git a/converters/indent/indent.c b/converters/indent/indent.c index 5cc8217..7b433dc 100644 --- a/converters/indent/indent.c +++ b/converters/indent/indent.c @@ -1,122 +1,122 @@ -/* This program converts a SIMH source to 4-tabbing - - Copyright (c) 2005, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#include -#include - -#define COMM_POS 56 - -int main (int argc, char *argv[]) -{ -int i, j, fill, itc, incomm, in8tab, ip, op; -char *ppos, iline[256], oline[256], oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: asc file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".new"); - else - strcat (oname, ".new"); - ifile = fopen (argv[i], "ra"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wa"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (incomm = in8tab = 0;;) { - for (j = 0; j < 256; j++) iline[j] = oline[j] = 0; - if (fgets (iline, 256, ifile) == NULL) break; - ip = 0; - if (!incomm) { - if (strncmp (iline, " ", 4) == 0) in8tab = 1; - else if (!isspace (iline[0]) && (iline[0] != '#') && strncmp (iline, "/*", 2)) in8tab = 0; - if ((strncmp (iline, "else {\t", 7) == 0) && - !isspace (iline[7])) { - fputs ("else {\n", ofile); - ip = 6; - } - else if ((strncmp (iline, "do {\t", 5) == 0) && - !isspace (iline[5])) { - fputs ("do {\n", ofile); - ip = 4; - } - } - for (itc = op = 0; iline[ip]; ip++) { - if (!incomm && (op == 0)) { - if (iline[ip] == '\t') { - itc++; - continue; - } - else if (itc) { - fill = (itc * 8) - (in8tab? 0: 4); - while (fill--) oline[op++] = ' '; - } - } - switch (iline[ip]) { - case '/': - if (!incomm && (iline[ip + 1] == '*')) { - incomm = 1; - if (ip != 0) { - while (op < COMM_POS) oline[op++] = ' '; - } - } - oline[op++] = iline[ip]; - break; - case '*': - if (incomm && (iline[ip + 1] == '/')) incomm = 0; - oline[op++] = iline[ip]; - break; - case '\t': - fill = 8 - (op % 8); - while (fill--) oline[op++] = ' '; - break; - case '\r': - break; - default: - oline[op++] = iline[ip]; - break; - } - } - if (op) fputs (oline, ofile); - } - fclose (ifile); - fclose (ofile); - } -return 0; -} +/* This program converts a SIMH source to 4-tabbing + + Copyright (c) 2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#include +#include + +#define COMM_POS 56 + +int main (int argc, char *argv[]) +{ +int i, j, fill, itc, incomm, in8tab, ip, op; +char *ppos, iline[256], oline[256], oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: asc file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".new"); + else + strcat (oname, ".new"); + ifile = fopen (argv[i], "ra"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wa"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (incomm = in8tab = 0;;) { + for (j = 0; j < 256; j++) iline[j] = oline[j] = 0; + if (fgets (iline, 256, ifile) == NULL) break; + ip = 0; + if (!incomm) { + if (strncmp (iline, " ", 4) == 0) in8tab = 1; + else if (!isspace (iline[0]) && (iline[0] != '#') && strncmp (iline, "/*", 2)) in8tab = 0; + if ((strncmp (iline, "else {\t", 7) == 0) && + !isspace (iline[7])) { + fputs ("else {\n", ofile); + ip = 6; + } + else if ((strncmp (iline, "do {\t", 5) == 0) && + !isspace (iline[5])) { + fputs ("do {\n", ofile); + ip = 4; + } + } + for (itc = op = 0; iline[ip]; ip++) { + if (!incomm && (op == 0)) { + if (iline[ip] == '\t') { + itc++; + continue; + } + else if (itc) { + fill = (itc * 8) - (in8tab? 0: 4); + while (fill--) oline[op++] = ' '; + } + } + switch (iline[ip]) { + case '/': + if (!incomm && (iline[ip + 1] == '*')) { + incomm = 1; + if (ip != 0) { + while (op < COMM_POS) oline[op++] = ' '; + } + } + oline[op++] = iline[ip]; + break; + case '*': + if (incomm && (iline[ip + 1] == '/')) incomm = 0; + oline[op++] = iline[ip]; + break; + case '\t': + fill = 8 - (op % 8); + while (fill--) oline[op++] = ' '; + break; + case '\r': + break; + default: + oline[op++] = iline[ip]; + break; + } + } + if (op) fputs (oline, ofile); + } + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/littcvt/littcvt.c b/converters/littcvt/littcvt.c index b2b4761..44266ab 100644 --- a/converters/littcvt/littcvt.c +++ b/converters/littcvt/littcvt.c @@ -1,71 +1,71 @@ -/* This program removes the 4 header bytes from a Litt tape - - Copyright (c) 1993-1999, Robert M. Supnik, Compaq Computer Corporation - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK, OR COMPAQ COMPUTER CORPORATION, BE LIABLE FOR ANY CLAIM, - DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik, or Compaq - Computer Corporation, shall not be used in advertising or otherwise to - promote the sale, use or other dealings in this Software without prior - written authorization from both the author and Compaq Computer Corporation. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, k, bc; -unsigned char buf[FLPSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".new"); - else - strcat (oname, ".new"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fread (&bc, sizeof (int), 1, ifile); - for (;;) { - k = fread (buf, sizeof (char), FLPSIZ, ifile); - if (k == 0) break; - fwrite (buf, sizeof (char), k, ofile); } - fclose (ifile); - fclose (ofile); -} - -exit (0); -} +/* This program removes the 4 header bytes from a Litt tape + + Copyright (c) 1993-1999, Robert M. Supnik, Compaq Computer Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK, OR COMPAQ COMPUTER CORPORATION, BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik, or Compaq + Computer Corporation, shall not be used in advertising or otherwise to + promote the sale, use or other dealings in this Software without prior + written authorization from both the author and Compaq Computer Corporation. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, bc; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".new"); + else + strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fread (&bc, sizeof (int), 1, ifile); + for (;;) { + k = fread (buf, sizeof (char), FLPSIZ, ifile); + if (k == 0) break; + fwrite (buf, sizeof (char), k, ofile); } + fclose (ifile); + fclose (ofile); +} + +exit (0); +} diff --git a/converters/m8376/m8376.c b/converters/m8376/m8376.c index 9af6a2c..d533f61 100644 --- a/converters/m8376/m8376.c +++ b/converters/m8376/m8376.c @@ -1,74 +1,74 @@ -/* This program assembles 8 PROM files into a 32bit binary file - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include - -static const int fnum[8] = { 60, 58, 74, 73, 90, 89, 106, 105 }; - -int main (int argc, char *argv[]) -{ -FILE *fi[8], *outf; -char fname[256]; -int c, i, j; -unsigned int wd[1024]; - -(void) argc; -(void) argv; - -for (i = 0; i < 8; i++) { - sprintf (fname, "C:\\temp\\m8376\\m8376e%03d.bin", fnum[i]); - fi[i] = fopen (fname, "rb"); - if (fi[i] == NULL) { - printf ("Can't open file %s\n", fname); - return 0; - } - } - -for (i = 0; i < 1024; i++) { - wd[i] = 0; - for (j = 7; j >=0; j--) { - c = fgetc (fi[j]); - if (c == EOF) { - printf ("Premature end of file on file %d\n", i); - return 0; - } - wd[i] = (wd[i] << 4) | (c & 0xF); - } - } - -for (i = 0; i < 8; i++) fclose (fi[i]); -outf = fopen ("c:\\prom.bin", "wb"); -if (outf == NULL) { - printf ("Can't open output file\n"); - return 0; - } -fwrite (wd, 1024, sizeof (int), outf); -fclose (outf); -return 0; -} +/* This program assembles 8 PROM files into a 32bit binary file + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include + +static const int fnum[8] = { 60, 58, 74, 73, 90, 89, 106, 105 }; + +int main (int argc, char *argv[]) +{ +FILE *fi[8], *outf; +char fname[256]; +int c, i, j; +unsigned int wd[1024]; + +(void) argc; +(void) argv; + +for (i = 0; i < 8; i++) { + sprintf (fname, "C:\\temp\\m8376\\m8376e%03d.bin", fnum[i]); + fi[i] = fopen (fname, "rb"); + if (fi[i] == NULL) { + printf ("Can't open file %s\n", fname); + return 0; + } + } + +for (i = 0; i < 1024; i++) { + wd[i] = 0; + for (j = 7; j >=0; j--) { + c = fgetc (fi[j]); + if (c == EOF) { + printf ("Premature end of file on file %d\n", i); + return 0; + } + wd[i] = (wd[i] << 4) | (c & 0xF); + } + } + +for (i = 0; i < 8; i++) fclose (fi[i]); +outf = fopen ("c:\\prom.bin", "wb"); +if (outf == NULL) { + printf ("Can't open output file\n"); + return 0; + } +fwrite (wd, 1024, sizeof (int), outf); +fclose (outf); +return 0; +} diff --git a/converters/mt2tpc/mt2tpc.c b/converters/mt2tpc/mt2tpc.c index f820c9e..cb3b367 100644 --- a/converters/mt2tpc/mt2tpc.c +++ b/converters/mt2tpc/mt2tpc.c @@ -1,89 +1,89 @@ -/* This program converts a simh simulated magtape to TPC format - - Copyright (c) 1993-1999, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, k, fc, wc, rc; -unsigned char bc[4] = { 0 }; -unsigned char buf[FLPSIZ]; -char *progname = argv[0]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if (argc < 2) { - fprintf (stderr, "Usage: %s file {file2 ...}\n", progname); - fprintf (stderr, "This will create a TPC tape image file named file.tpc from the input simh image file\n"); - exit (0); - } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".tpc"); - else - strcat (oname, ".tpc"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fc = 1; rc = 0; - for (;;) { - k = fread (bc, sizeof (char), 4, ifile); - if (k == 0) break; - if (bc[2] | bc[3]) { - printf ("Invalid record size, record %d, size = 0x%02X%02X%02X%02X\n", rc, bc[3], bc[2], bc[1], bc[0]); - break; } - wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; - wc = (wc + 1) & ~1; - fwrite (bc, sizeof (char), 2, ofile); - if (wc) { - k = fread (buf, sizeof (char), wc, ifile); - for ( ; k < wc; k++) buf[k] =0; - fwrite (buf, sizeof (char), wc, ofile); - k = fread (bc, sizeof (char), 4, ifile); - rc++; } - else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); - else printf ("End of tape\n"); - fc++; - rc = 0; } - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts a simh simulated magtape to TPC format + + Copyright (c) 1993-1999, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, fc, wc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *progname = argv[0]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if (argc < 2) { + fprintf (stderr, "Usage: %s file {file2 ...}\n", progname); + fprintf (stderr, "This will create a TPC tape image file named file.tpc from the input simh image file\n"); + exit (0); + } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".tpc"); + else + strcat (oname, ".tpc"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 4, ifile); + if (k == 0) break; + if (bc[2] | bc[3]) { + printf ("Invalid record size, record %d, size = 0x%02X%02X%02X%02X\n", rc, bc[3], bc[2], bc[1], bc[0]); + break; } + wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + wc = (wc + 1) & ~1; + fwrite (bc, sizeof (char), 2, ofile); + if (wc) { + k = fread (buf, sizeof (char), wc, ifile); + for ( ; k < wc; k++) buf[k] =0; + fwrite (buf, sizeof (char), wc, ofile); + k = fread (bc, sizeof (char), 4, ifile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvt23/mtcvtv23.c b/converters/mtcvt23/mtcvtv23.c index da927c1..4c0bd3d 100644 --- a/converters/mtcvt23/mtcvtv23.c +++ b/converters/mtcvt23/mtcvtv23.c @@ -1,83 +1,83 @@ -/* This program converts a pre V2.3 simulated magtape to a V2.3 magtape - - Copyright (c) 1993-1999, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, k, wc, fc, rc; -unsigned char bc[4] = { 0 }; -unsigned char buf[FLPSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".tap"); - else - strcat (oname, ".tap"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fc = 1; rc = 0; - for (;;) { - k = fread (bc, sizeof (char), 2, ifile); - if (k == 0) break; - wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; - wc = (wc + 1) & ~1; - fwrite (bc, sizeof (char), 4, ofile); - if (wc) { - k = fread (buf, sizeof (char), wc, ifile); - for ( ; k < wc; k++) buf[k] =0; - fwrite (buf, sizeof (char), wc, ofile); - fwrite (bc, sizeof (char), 4, ofile); - rc++; } - else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); - else printf ("End of tape\n"); - fc++; - rc = 0; } - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts a pre V2.3 simulated magtape to a V2.3 magtape + + Copyright (c) 1993-1999, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, wc, fc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".tap"); + else + strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 2, ifile); + if (k == 0) break; + wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + wc = (wc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (wc) { + k = fread (buf, sizeof (char), wc, ifile); + for ( ; k < wc; k++) buf[k] =0; + fwrite (buf, sizeof (char), wc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvt23/mtcvtv23.txt b/converters/mtcvt23/mtcvtv23.txt index 41b027d..af5227e 100644 --- a/converters/mtcvt23/mtcvtv23.txt +++ b/converters/mtcvt23/mtcvtv23.txt @@ -1,35 +1,35 @@ -mtcvtv23 converts a tape images in .tpc format to .tap format. - -.tpc format tape images have the format - - 2 byte record length 1 - record 1 - 2 byte record length 2 - record 2 - : - 2 bytes = 0000 for end of file - -and so on. This is the format produced by various UNIX utilities -for dumping tape images. - -.tap format tape images have the format - - 4 byte record length 1 - record 1 - repeat of 4 byte record length 1 - 4 byte record length 2 - record 2 - repeat of 4 byte record length 2 - : - 4 bytes = 00000000 for end of file - -and so on. This is the tape format expected by simh, Tim Stark's TS10, -and John Wilson's E11. - -mtcvtv23 is invoked by - - mtcvtv23 file1 file2 file3... - -Each file in turn is converted from .tpc format to .tap format. The -input file can have any extension; the converted file will have a .tap +mtcvtv23 converts a tape images in .tpc format to .tap format. + +.tpc format tape images have the format + + 2 byte record length 1 + record 1 + 2 byte record length 2 + record 2 + : + 2 bytes = 0000 for end of file + +and so on. This is the format produced by various UNIX utilities +for dumping tape images. + +.tap format tape images have the format + + 4 byte record length 1 + record 1 + repeat of 4 byte record length 1 + 4 byte record length 2 + record 2 + repeat of 4 byte record length 2 + : + 4 bytes = 00000000 for end of file + +and so on. This is the tape format expected by simh, Tim Stark's TS10, +and John Wilson's E11. + +mtcvtv23 is invoked by + + mtcvtv23 file1 file2 file3... + +Each file in turn is converted from .tpc format to .tap format. The +input file can have any extension; the converted file will have a .tap extension. \ No newline at end of file diff --git a/converters/mtcvtfix/mtcvtfix.c b/converters/mtcvtfix/mtcvtfix.c index cae0b29..b7fd991 100644 --- a/converters/mtcvtfix/mtcvtfix.c +++ b/converters/mtcvtfix/mtcvtfix.c @@ -1,96 +1,96 @@ -/* This program fixes a SIMH magtape containing a misread EOF - - Copyright (c) 2003, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, fc, rc; -unsigned int k, tbc; -unsigned char bc[4] = { 0 }; -unsigned char bceof[4] = { 0 }; -unsigned char buf[FLPSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".new"); - else - strcat (oname, ".new"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fc = 1; rc = 0; - for (;;) { - k = fread (bc, sizeof (char), 4, ifile); - if (k == 0) break; - tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | - ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; - if (tbc) { - printf ("Record size = %d\n", tbc); - if (tbc > FLPSIZ) { - printf ("Record too big\n"); - return 0; } - k = fread (buf, sizeof (char), tbc, ifile); - for ( ; k < tbc; k++) buf[k] = 0; - fread (bc, sizeof (char), 4, ifile); - if (tbc > 1) { - fwrite (bc, sizeof (char), 4, ofile); - fwrite (buf, sizeof (char), tbc, ofile); - fwrite (bc, sizeof (char), 4, ofile); - rc++; } - else { - printf ("Record length = 1, ignored\n"); - } } - else { - fwrite (bceof, sizeof (char), 4, ofile); - if (rc) printf ("End of file %d, record count = %d\n", fc, rc); - else printf ("End of tape\n"); - fc++; - rc = 0; } - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program fixes a SIMH magtape containing a misread EOF + + Copyright (c) 2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, fc, rc; +unsigned int k, tbc; +unsigned char bc[4] = { 0 }; +unsigned char bceof[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".new"); + else + strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 4, ifile); + if (k == 0) break; + tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | + ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + if (tbc) { + printf ("Record size = %d\n", tbc); + if (tbc > FLPSIZ) { + printf ("Record too big\n"); + return 0; } + k = fread (buf, sizeof (char), tbc, ifile); + for ( ; k < tbc; k++) buf[k] = 0; + fread (bc, sizeof (char), 4, ifile); + if (tbc > 1) { + fwrite (bc, sizeof (char), 4, ofile); + fwrite (buf, sizeof (char), tbc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { + printf ("Record length = 1, ignored\n"); + } } + else { + fwrite (bceof, sizeof (char), 4, ofile); + if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/mtcvtodd/mtcvtodd.c b/converters/mtcvtodd/mtcvtodd.c index 1dfce2c..89fda4a 100644 --- a/converters/mtcvtodd/mtcvtodd.c +++ b/converters/mtcvtodd/mtcvtodd.c @@ -1,89 +1,89 @@ -/* This program converts an E11 magtape (with odd record sizes) to SIMH - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, k, tbc, ebc, fc, rc; -unsigned char bc[4] = { 0 }; -unsigned char buf[FLPSIZ]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".new"); - else - strcat (oname, ".new"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fc = 1; rc = 0; - for (;;) { - k = fread (bc, sizeof (char), 4, ifile); - if (k == 0) break; - tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | - ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; - ebc = (tbc + 1) & ~1; - fwrite (bc, sizeof (char), 4, ofile); - if (tbc) { - printf ("Record size = %d\n", tbc); - if (tbc > FLPSIZ) { - printf ("Record too big\n"); - return 0; } - k = fread (buf, sizeof (char), tbc, ifile); - for ( ; k < ebc; k++) buf[k] = 0; - fread (bc, sizeof (char), 4, ifile); - fwrite (buf, sizeof (char), ebc, ofile); - fwrite (bc, sizeof (char), 4, ofile); - rc++; } - else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); - else printf ("End of tape\n"); - fc++; - rc = 0; } - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts an E11 magtape (with odd record sizes) to SIMH + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, tbc, ebc, fc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".new"); + else + strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 4, ifile); + if (k == 0) break; + tbc = ((unsigned int) bc[3] << 24) | ((unsigned int) bc[2] << 16) | + ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + ebc = (tbc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (tbc) { + printf ("Record size = %d\n", tbc); + if (tbc > FLPSIZ) { + printf ("Record too big\n"); + return 0; } + k = fread (buf, sizeof (char), tbc, ifile); + for ( ; k < ebc; k++) buf[k] = 0; + fread (bc, sizeof (char), 4, ifile); + fwrite (buf, sizeof (char), ebc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/noff/noff.c b/converters/noff/noff.c index ec2b8c7..38945ad 100644 --- a/converters/noff/noff.c +++ b/converters/noff/noff.c @@ -1,68 +1,68 @@ -/* This program strips FF from a source listing - - Copyright (c) 2005, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ -int i, c, ffc; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: asc file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".new"); - else - strcat (oname, ".new"); - ifile = fopen (argv[i], "ra"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wa"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (ffc = 0; (c = fgetc (ifile)) != EOF; ) { - if (c == '\f') ffc++; - else fputc (c, ofile); - } - if (ffc) printf ("Form feeds removed: %d\n", ffc); - fclose (ifile); - fclose (ofile); - } -return 0; -} +/* This program strips FF from a source listing + + Copyright (c) 2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ +int i, c, ffc; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: asc file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".new"); + else + strcat (oname, ".new"); + ifile = fopen (argv[i], "ra"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wa"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (ffc = 0; (c = fgetc (ifile)) != EOF; ) { + if (c == '\f') ffc++; + else fputc (c, ofile); + } + if (ffc) printf ("Form feeds removed: %d\n", ffc); + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/sfmtcvt/sfmtcvt.c b/converters/sfmtcvt/sfmtcvt.c index ad68fb5..93f8edc 100644 --- a/converters/sfmtcvt/sfmtcvt.c +++ b/converters/sfmtcvt/sfmtcvt.c @@ -1,118 +1,118 @@ -/* This program converts a Motorola S format PROM dump to a binary file - - Copyright (c) 1993-2003, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define HEX(a) ((a) >= 'A'? (a) - 'A' + 10: (a) - '0') -#define MAXA (2 << 14) -#define MAXR 4 - -int main (int argc, char *argv[]) -{ -int i, d, d1, j, k, astrt, dstrt, addr, maxaddr[MAXR]; -int numr, numf; -unsigned char data[MAXR][MAXA]; -char *s, *ppos, *cptr, line[256], oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -s = argv[1]; -if ((s != NULL) && (*s++ == '-')) { - ++argv; --argc; - switch (*s) { - case '1': - numr = 1; break; - case '2': - numr = 2; break; - case '4': - numr = 4; break; - default: - fprintf (stderr, "Bad option %c\n", *s); - return 0; } - } -else numr = 1; - -for (i = 1, numf = 0; i < argc; i++) { - ifile = fopen (argv[i], "r"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - astrt = 4; - maxaddr[numf] = 0; - for (;;) { - cptr = fgets (line, 256, ifile); - if (cptr == NULL) break; - if (line[0] != 'S') continue; - if (line[1] == '1') dstrt = 8; - else if (line[1] == '2') dstrt = 10; - else continue; - for (k = astrt, addr = 0; k < dstrt; k++) { - d = HEX (line[k]); - addr = (addr << 4) + d; } - if (addr >= MAXA) { - printf ("Address %o out of range\n", addr); - break; } - for (k = dstrt; k < (dstrt + 32); k = k + 2, addr++) { - d = HEX (line[k]); - d1 = HEX (line[k+1]); - data[numf][addr] = (d << 4) + d1; } - if (addr > maxaddr[numf]) maxaddr[numf] = addr; - } - fclose (ifile); - numf++; - if (numf >= numr) { - for (k = 0; k < numr; k++) { - if (maxaddr[k] != maxaddr[0]) { - printf ("Rom lengths don't match, file 1 = %d, file %d = %d\n", - maxaddr[0], k, maxaddr[k]); - return 0; } } - strcpy (oname, argv[i]); - if ((ppos = strrchr (oname, '.'))) - strcpy (ppos, ".bin"); - else - strcat (oname, ".bin"); - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - printf ("Output file: %s, ROM size is %d\n", oname, maxaddr[0]); - for (k = 0; k < maxaddr[0]; k++) { - for (j = numr - 1; j >= 0; j--) { - fwrite (&data[j][k], 1, 1, ofile); } } - fclose (ofile); - numf = 0; - } - } -if (numf) printf ("Unprocessed files\n"); -return 0; -} +/* This program converts a Motorola S format PROM dump to a binary file + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define HEX(a) ((a) >= 'A'? (a) - 'A' + 10: (a) - '0') +#define MAXA (2 << 14) +#define MAXR 4 + +int main (int argc, char *argv[]) +{ +int i, d, d1, j, k, astrt, dstrt, addr, maxaddr[MAXR]; +int numr, numf; +unsigned char data[MAXR][MAXA]; +char *s, *ppos, *cptr, line[256], oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case '1': + numr = 1; break; + case '2': + numr = 2; break; + case '4': + numr = 4; break; + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; } + } +else numr = 1; + +for (i = 1, numf = 0; i < argc; i++) { + ifile = fopen (argv[i], "r"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + astrt = 4; + maxaddr[numf] = 0; + for (;;) { + cptr = fgets (line, 256, ifile); + if (cptr == NULL) break; + if (line[0] != 'S') continue; + if (line[1] == '1') dstrt = 8; + else if (line[1] == '2') dstrt = 10; + else continue; + for (k = astrt, addr = 0; k < dstrt; k++) { + d = HEX (line[k]); + addr = (addr << 4) + d; } + if (addr >= MAXA) { + printf ("Address %o out of range\n", addr); + break; } + for (k = dstrt; k < (dstrt + 32); k = k + 2, addr++) { + d = HEX (line[k]); + d1 = HEX (line[k+1]); + data[numf][addr] = (d << 4) + d1; } + if (addr > maxaddr[numf]) maxaddr[numf] = addr; + } + fclose (ifile); + numf++; + if (numf >= numr) { + for (k = 0; k < numr; k++) { + if (maxaddr[k] != maxaddr[0]) { + printf ("Rom lengths don't match, file 1 = %d, file %d = %d\n", + maxaddr[0], k, maxaddr[k]); + return 0; } } + strcpy (oname, argv[i]); + if ((ppos = strrchr (oname, '.'))) + strcpy (ppos, ".bin"); + else + strcat (oname, ".bin"); + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + printf ("Output file: %s, ROM size is %d\n", oname, maxaddr[0]); + for (k = 0; k < maxaddr[0]; k++) { + for (j = numr - 1; j >= 0; j--) { + fwrite (&data[j][k], 1, 1, ofile); } } + fclose (ofile); + numf = 0; + } + } +if (numf) printf ("Unprocessed files\n"); +return 0; +} diff --git a/converters/strrem/strrem.c b/converters/strrem/strrem.c index 22b0996..2400151 100644 --- a/converters/strrem/strrem.c +++ b/converters/strrem/strrem.c @@ -1,72 +1,72 @@ -/* This program removes a string - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include - -const char *srem = "e-drive\\"; - -int main (int argc, char *argv[]) -{ -int i, slen; -char *ppos, *fpos, *tpos, oname[256], line[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: strrem file [file...]\n"); - exit (0); } - -slen = strlen (srem); -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); - else strcat (oname, ".new"); - ifile = fopen (argv[i], "r"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "w"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - while (fgets (line, 255, ifile) != NULL) { - tpos = strstr (line, srem); - if (tpos) { - fpos = tpos + slen; - while (*fpos) *tpos++ = *fpos++; - *tpos = 0; - } - fputs (line, ofile); - } - fclose (ifile); - fclose (ofile); - } -return 0; -} +/* This program removes a string + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include + +const char *srem = "e-drive\\"; + +int main (int argc, char *argv[]) +{ +int i, slen; +char *ppos, *fpos, *tpos, oname[256], line[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: strrem file [file...]\n"); + exit (0); } + +slen = strlen (srem); +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "r"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "w"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + while (fgets (line, 255, ifile) != NULL) { + tpos = strstr (line, srem); + if (tpos) { + fpos = tpos + slen; + while (*fpos) *tpos++ = *fpos++; + *tpos = 0; + } + fputs (line, ofile); + } + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/strsub/strsub.c b/converters/strsub/strsub.c index ff4503c..ee18ed1 100644 --- a/converters/strsub/strsub.c +++ b/converters/strsub/strsub.c @@ -1,74 +1,74 @@ -/* This program removes a string - - Copyright (c) 1993-2001, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ -int i, rmvlen; -char *ppos, *fpos, *tpos, *rmv, *ins, oname[256], line[256]; -FILE *ifile, *ofile; - -if ((argc < 4) || (argv[0] == NULL)) { - printf ("Usage is: strsub s1 s2 file [file...]\n"); - exit (0); } - -rmv = argv[1]; -rmvlen = strlen (rmv); -ins = argv[2]; -for (i = 3; i < argc; i++) { - strcpy (oname, argv[i]); - if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); - else strcat (oname, ".new"); - ifile = fopen (argv[i], "r"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "w"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - while (fgets (line, 255, ifile) != NULL) { - tpos = strstr (line, rmv); - if (tpos) { - *tpos = 0; - fputs (line, ofile); - fputs (ins, ofile); - fpos = tpos + rmvlen; - fputs (fpos, ofile); - } - else fputs (line, ofile); - } - fclose (ifile); - fclose (ofile); - } -return 0; -} +/* This program removes a string + + Copyright (c) 1993-2001, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ +int i, rmvlen; +char *ppos, *fpos, *tpos, *rmv, *ins, oname[256], line[256]; +FILE *ifile, *ofile; + +if ((argc < 4) || (argv[0] == NULL)) { + printf ("Usage is: strsub s1 s2 file [file...]\n"); + exit (0); } + +rmv = argv[1]; +rmvlen = strlen (rmv); +ins = argv[2]; +for (i = 3; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "r"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "w"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + while (fgets (line, 255, ifile) != NULL) { + tpos = strstr (line, rmv); + if (tpos) { + *tpos = 0; + fputs (line, ofile); + fputs (ins, ofile); + fpos = tpos + rmvlen; + fputs (fpos, ofile); + } + else fputs (line, ofile); + } + fclose (ifile); + fclose (ofile); + } +return 0; +} diff --git a/converters/tar2mt/tar2mt.c b/converters/tar2mt/tar2mt.c index 64be3e9..546f751 100644 --- a/converters/tar2mt/tar2mt.c +++ b/converters/tar2mt/tar2mt.c @@ -1,92 +1,92 @@ -/* This program converts a tar file to a simulated 8192B blocked magtape - - Copyright (c) 2015, Mark Pizzolato - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Mark Pizzolato shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Mark Pizzolato. -*/ - -#include -#include -#include -#include - -main (int argc, char **argv) -{ -FILE *fIn = NULL, *fOut = NULL; -size_t blocksize = 8192, bytes_read; -unsigned char *br = (unsigned char *)&bytes_read; -char *progname = argv[0]; -char *buf = NULL; -char *tapname = NULL; - -if (argc < 2) { - fprintf (stderr, "Usage: %s {-b blocksize} tarfile {tarfile2 ...}\n", progname); - fprintf (stderr, "This will create a simh tap file named tarfile.tap from the input tar file\n"); - fprintf (stderr, "blocksize defaults to 8192\n"); - exit (0); - } -if ((argc >= 3) && ((strcmp("-b", argv[1]) == 0) || (strcmp("--blocksize", argv[1]) == 0))) { - if (atoi (argv[2]) <= 0) { - fprintf (stderr, "Invalid blocksize: %s\n", argv[2]); - exit (0); - } - blocksize = (size_t)atoi (argv[2]); - argc -= 2; - argv += 2; - } -while (--argc > 0) { - ++argv; - tapname = realloc (tapname, 5 + strlen (argv[0])); - if (NULL == (fIn = fopen (argv[0], "rb"))) { - fprintf (stderr, "Error Opening tar file '%s': %s\n", argv[0], strerror (errno)); - break; - } - strcpy (tapname, argv[0]); - strcat (tapname, ".tap"); - if (NULL == (fOut = fopen (tapname, "wb"))) { - fprintf (stderr, "Error Opening tap file '%s': %s\n", tapname, strerror (errno)); - break; - } - buf = realloc (buf, blocksize); - while (1) { - bytes_read = fread (buf, 1, blocksize, fIn); - if (bytes_read == 0) - break; - fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); - fwrite (buf, 1, bytes_read, fOut); - fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); - } - fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); - fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); - fclose (fIn); - fIn = NULL; - fclose (fOut); - fOut = NULL; - } -if (fIn) - fclose (fIn); -if (fOut) - fclose (fOut); -free (tapname); -free (buf); -exit(0); -} +/* This program converts a tar file to a simulated 8192B blocked magtape + + Copyright (c) 2015, Mark Pizzolato + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Mark Pizzolato shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Mark Pizzolato. +*/ + +#include +#include +#include +#include + +main (int argc, char **argv) +{ +FILE *fIn = NULL, *fOut = NULL; +size_t blocksize = 8192, bytes_read; +unsigned char *br = (unsigned char *)&bytes_read; +char *progname = argv[0]; +char *buf = NULL; +char *tapname = NULL; + +if (argc < 2) { + fprintf (stderr, "Usage: %s {-b blocksize} tarfile {tarfile2 ...}\n", progname); + fprintf (stderr, "This will create a simh tap file named tarfile.tap from the input tar file\n"); + fprintf (stderr, "blocksize defaults to 8192\n"); + exit (0); + } +if ((argc >= 3) && ((strcmp("-b", argv[1]) == 0) || (strcmp("--blocksize", argv[1]) == 0))) { + if (atoi (argv[2]) <= 0) { + fprintf (stderr, "Invalid blocksize: %s\n", argv[2]); + exit (0); + } + blocksize = (size_t)atoi (argv[2]); + argc -= 2; + argv += 2; + } +while (--argc > 0) { + ++argv; + tapname = realloc (tapname, 5 + strlen (argv[0])); + if (NULL == (fIn = fopen (argv[0], "rb"))) { + fprintf (stderr, "Error Opening tar file '%s': %s\n", argv[0], strerror (errno)); + break; + } + strcpy (tapname, argv[0]); + strcat (tapname, ".tap"); + if (NULL == (fOut = fopen (tapname, "wb"))) { + fprintf (stderr, "Error Opening tap file '%s': %s\n", tapname, strerror (errno)); + break; + } + buf = realloc (buf, blocksize); + while (1) { + bytes_read = fread (buf, 1, blocksize, fIn); + if (bytes_read == 0) + break; + fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); + fwrite (buf, 1, bytes_read, fOut); + fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); + } + fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); + fputc (br[0], fOut); fputc (br[1], fOut); fputc (br[2], fOut); fputc (br[3], fOut); + fclose (fIn); + fIn = NULL; + fclose (fOut); + fOut = NULL; + } +if (fIn) + fclose (fIn); +if (fOut) + fclose (fOut); +free (tapname); +free (buf); +exit(0); +} diff --git a/converters/tar2mt/tar2mt.txt b/converters/tar2mt/tar2mt.txt index ae13dbe..5d06fe0 100644 --- a/converters/tar2mt/tar2mt.txt +++ b/converters/tar2mt/tar2mt.txt @@ -1,25 +1,25 @@ -tar2mt converts a tar file to a simh .tap tape image. - -.tap format tape images have the format - - 4 byte record length 1 - record 1 - repeat of 4 byte record length 1 - 4 byte record length 2 - record 2 - repeat of 4 byte record length 2 - : - 4 bytes = 00000000 for end of file - -and so on. This is the tape format expected by simh, Tim Stark's TS10, -and John Wilson's E11. - -tar2mt is invoked by - - tar2mt {-b blocksize} file1 file2 file3... - -blocksize defaults to 8192. - -Each file in turn is converted from a tar file to a .tap tape image. The -input file can have any extension; the converted file will have a .tap +tar2mt converts a tar file to a simh .tap tape image. + +.tap format tape images have the format + + 4 byte record length 1 + record 1 + repeat of 4 byte record length 1 + 4 byte record length 2 + record 2 + repeat of 4 byte record length 2 + : + 4 bytes = 00000000 for end of file + +and so on. This is the tape format expected by simh, Tim Stark's TS10, +and John Wilson's E11. + +tar2mt is invoked by + + tar2mt {-b blocksize} file1 file2 file3... + +blocksize defaults to 8192. + +Each file in turn is converted from a tar file to a .tap tape image. The +input file can have any extension; the converted file will have a .tap extension. \ No newline at end of file diff --git a/converters/tp512cvt/tp512cvt.c b/converters/tp512cvt/tp512cvt.c index cd5af60..f0fab06 100644 --- a/converters/tp512cvt/tp512cvt.c +++ b/converters/tp512cvt/tp512cvt.c @@ -1,76 +1,76 @@ -/* This program converts a tp data file to a simulated 512B blocked magtape - - Copyright (c) 1993-2003, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define BLKSIZ 512 -int main (int argc, char *argv[]) -{ -int i, k; -unsigned char buf[BLKSIZ]; -unsigned char tef[4] = { 0, 0, 0, 0 }; -unsigned char twc[4] = { 0, 2, 0, 0 }; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); - else strcat (oname, ".tap"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - for (;;) { - k = fread (buf, sizeof (char), BLKSIZ, ifile); - if (k == 0) break; - if (k != BLKSIZ) { - printf ("Short block, size = %d\n", k); - for ( ; k < BLKSIZ; k++) buf[k] = 0; } - fwrite (twc, sizeof (char), 4, ofile); - fwrite (buf, sizeof (char), BLKSIZ, ofile); - fwrite (twc, sizeof (char), 4, ofile); - } - fwrite (tef, sizeof (char), 4, ofile); - fwrite (tef, sizeof (char), 4, ofile); - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts a tp data file to a simulated 512B blocked magtape + + Copyright (c) 1993-2003, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define BLKSIZ 512 +int main (int argc, char *argv[]) +{ +int i, k; +unsigned char buf[BLKSIZ]; +unsigned char tef[4] = { 0, 0, 0, 0 }; +unsigned char twc[4] = { 0, 2, 0, 0 }; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + for (;;) { + k = fread (buf, sizeof (char), BLKSIZ, ifile); + if (k == 0) break; + if (k != BLKSIZ) { + printf ("Short block, size = %d\n", k); + for ( ; k < BLKSIZ; k++) buf[k] = 0; } + fwrite (twc, sizeof (char), 4, ofile); + fwrite (buf, sizeof (char), BLKSIZ, ofile); + fwrite (twc, sizeof (char), 4, ofile); + } + fwrite (tef, sizeof (char), 4, ofile); + fwrite (tef, sizeof (char), 4, ofile); + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/converters/tpc2mt/tpc2mt.c b/converters/tpc2mt/tpc2mt.c index 3c5896d..8796127 100644 --- a/converters/tpc2mt/tpc2mt.c +++ b/converters/tpc2mt/tpc2mt.c @@ -1,84 +1,84 @@ -/* This program converts a TPC simulated magtape to simh format - - Copyright (c) 2015, Robert M. Supnik, Mark Pizzolato - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ - -#include -#include -#include -#include -#define FLPSIZ 65536 -int main (int argc, char *argv[]) -{ -int i, k, fc, wc, rc; -unsigned char bc[4] = { 0 }; -unsigned char buf[FLPSIZ]; -char *progname = argv[0]; -char *ppos, oname[256]; -FILE *ifile, *ofile; - -if (argc < 2) { - fprintf (stderr, "Usage: %s file {file2 ...}\n", progname); - fprintf (stderr, "This will create a simh tap file named file.tap from the input tpc image file\n"); - exit (0); - } - -for (i = 1; i < argc; i++) { - strcpy (oname, argv[i]); - if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); - else strcat (oname, ".tap"); - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - ofile = fopen (oname, "wb"); - if (ofile == NULL) { - printf ("Error opening file: %s\n", oname); - exit (0); } - - printf ("Processing file %s\n", argv[i]); - fc = 1; rc = 0; - for (;;) { - k = fread (bc, sizeof (char), 2, ifile); - if (k == 0) break; - wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; - wc = (wc + 1) & ~1; - fwrite (bc, sizeof (char), 4, ofile); - if (wc) { - k = fread (buf, sizeof (char), ((unsigned int) bc[1] << 8) | (unsigned int) bc[0], ifile); - for ( ; k < wc; k++) buf[k] =0; - fwrite (buf, sizeof (char), wc, ofile); - k = fwrite (bc, sizeof (char), 4, ofile); - rc++; } - else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); - else printf ("End of tape\n"); - fc++; - rc = 0; } - } - fclose (ifile); - fclose (ofile); - } - -return 0; -} +/* This program converts a TPC simulated magtape to simh format + + Copyright (c) 2015, Robert M. Supnik, Mark Pizzolato + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, fc, wc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *progname = argv[0]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if (argc < 2) { + fprintf (stderr, "Usage: %s file {file2 ...}\n", progname); + fprintf (stderr, "This will create a simh tap file named file.tap from the input tpc image file\n"); + exit (0); + } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".tap"); + else strcat (oname, ".tap"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 2, ifile); + if (k == 0) break; + wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + wc = (wc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (wc) { + k = fread (buf, sizeof (char), ((unsigned int) bc[1] << 8) | (unsigned int) bc[0], ifile); + for ( ; k < wc; k++) buf[k] =0; + fwrite (buf, sizeof (char), wc, ofile); + k = fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/crossassemblers/hpasm/hpasm.c b/crossassemblers/hpasm/hpasm.c index 69828ca..1dd5517 100644 --- a/crossassemblers/hpasm/hpasm.c +++ b/crossassemblers/hpasm/hpasm.c @@ -1,930 +1,930 @@ -/* -** HPASM.C -- Assembler for HP2100 -*/ -/****************************************************/ -#include -#include -#include -/****************************************************/ -#define MXSYM 1500 -#define MXLABEL 5 -#define ALLOW_SPECIALS_IN_LABEL 1 -/****************************************************/ -typedef struct { - char name[MXLABEL+1]; - long value; -} SYM; -/****************************************************/ -long nbsyms; -SYM symtab[MXSYM]; -long pass; -long addr; -char line[80]; -long lp; -long line_count; -long err_count; -long print_flag; /* =1 if current line has been printed */ -int ifn_flag=0; /* =1 if IFN does not skip */ -int ifz_flag=0; /* =1 if IFZ does not skip */ -int xif_flag=0; /* =1 if we are skipping until XIF */ -int rep_count=0; /* >0 if REP statement in process */ -/****************************************************/ -/* -** Variables for listing output routines -*/ -char listfilename[256]; -FILE *listfile; -/****************************************************/ -start_listing (nam) -/* -** Initialize listing output file -*/ -char *nam; -{ - char *ppos; - - strncpy (listfilename, nam, 250); - if (ppos = strrchr (listfilename, '.')) strcpy (ppos, ".lst"); - else strcat (listfilename, ".lst"); - listfile = fopen (listfilename, "w"); -} -/****************************************************/ -finish_listing () { -/* -** Finish off listing output file -*/ - fclose (listfile); - printf ("Listing is in %s\n", listfilename); -} -/****************************************************/ -/* -** Variables for binary output routines -*/ -char outfilename[256]; -FILE *outfile; -long outaddr; -long outcount; -long outbufsize=27; -long outbuf[27]; -/****************************************************/ -start_output (nam) -/* -** Initialize binary output file -*/ -char *nam; -{ - char *ppos; - - strncpy (outfilename, nam, 250); - if (ppos = strrchr (outfilename, '.')) strcpy (ppos, ".bin"); - else strcat (outfilename, ".bin"); - outfile = fopen (outfilename, "wb"); - outaddr = 0; - outcount = 0; -} -/****************************************************/ -send_output () { -/* -** Write block to binary output file -*/ - long ii,sum; - - fputc (outcount, outfile); - fputc (0, outfile); - fputc (outaddr >> 8, outfile); - fputc (outaddr & 255, outfile); - sum = outaddr; - for (ii=0 ; ii < outcount ; ii++) { - fputc (outbuf[ii] >> 8, outfile); - fputc (outbuf[ii] & 255, outfile); - sum += outbuf[ii]; - } - fputc (sum >> 8, outfile); - fputc (sum & 255, outfile); - outcount = 0; -} -/****************************************************/ -output_word (addr,word) -/* -** Write word to binary output file -*/ -long addr; -long word; -{ - if (outcount && (outaddr + outcount) != addr) { - send_output(); - } - if (!outcount) outaddr = addr; - outbuf[outcount++] = word; - if (outcount == outbufsize) { - send_output(); - } -} -/****************************************************/ -finish_output () { -/* -** Finish current output block, write trailing leader, -** and close output file -*/ - int ii; - if (outcount) send_output(); - for (ii=0; ii<20; ii++) fputc (0, outfile); - fclose (outfile); - printf ("Output is in %s\n", outfilename); -} -/****************************************************/ -emit (code) -long code; -{ - fprintf (listfile, " %05lo %06lo ", addr, code); - if (!print_flag) { - fprintf (listfile, "%s", line); - print_flag = 1; - } - fprintf (listfile, "\n"); - - output_word (addr, code); - addr++; -} -/****************************************************/ -err (text) -char *text; -{ - if (!print_flag) { - fprintf (listfile, " %12s %s\n", " ", line); - print_flag = 1; - } - fprintf (listfile, "ERROR: %s\n", text); - err_count++; -} -/****************************************************/ -int is_valid_label_char (ch) -char ch; -{ - if (isalpha(ch)) return (1); - if (isdigit(ch)) return (1); - if (ch=='.') return (1); -#if ALLOW_SPECIALS_IN_LABEL - if (ch=='&') return (1); - if (ch=='?') return (1); - if (ch=='#') return (1); - if (ch=='/') return (1); - if (ch=='%') return (1); - if (ch=='$') return (1); - if (ch=='[') return (1); - if (ch=='@') return (1); - if (ch=='!') return (1); - if (ch=='^') return (1); -#endif - return (0); -} -/****************************************************/ -void insert_label (label, value) -char *label; -long value; -/* -** Insert label into symbol table -*/ -{ - long i; - if (strlen(label)>MXLABEL) { - err ("Symbol name > 5"); - return; - } - for (i=0; i= 1.0) { num = num / 2; exp++; } - while (num < 0.5) { num = num * 2; exp--; } - - if (neg) num = -num; - - if (neg) num += (0177 / 65536.0 / 65536.0); - else num += (0200 / 65536.0 / 65536.0); - - if (neg && num >= -0.5) { num = num * 2; exp--; } - if (!neg && num >= 1.0) { num = num / 2; exp++; } - - if (exp < -128) return (-2); - - if (exp > 127) { - if (neg) { *a = 0x8000; *b = 0x01FE; } - else { *a = 0x7FFF; *b = 0xFFFE; } - return (-3); - } - - frac = num * 65536.0 * 128.0 * 256.0; - exp = exp << 1; - if (exp < 0) exp++; - *a = (frac >> 16) & 0xFFFF; - *b = (frac & 0xFF00) | (exp & 0x00FF); - - return (0); -} -/****************************************************/ -parse_digits (base, out) -int base; -long *out; -{ - long dig, val; - val = 0; - dig = line[lp] - '0'; - while (dig>=0 && digMXLABEL) { - err ("Illegal symbol"); - *out=0; - } else { - sym[count]=0; - find_label (sym, out); - } -} -/****************************************************/ -parse_term (out) -long *out; -{ - char ch; - ch = line[lp]; - if (isdigit(ch)) parse_const (out); - else if (is_valid_label_char(ch)) parse_sym (out); - else if (ch=='*') { lp++; *out = addr; } - else { - err ("Syntax error in expression"); - *out=0; - } -} -/****************************************************/ -parse_neg (out) -long *out; -{ - long temp; - if (line[lp]=='-') { - lp++; - parse_term (&temp); - *out = (-temp) & 0xFFFF; - } else if (line[lp]=='+') { - lp++; - parse_term (out); - } else { - parse_term (out); - } -} -/****************************************************/ -parse_sum (out) -long *out; -{ - long v1,v2,op; - parse_neg (&v1); - while (line[lp]=='+' || line[lp]=='-') { - op = line[lp++]; - parse_neg (&v2); - if (op=='+') v1 = v1 + v2; - if (op=='-') v1 = v1 - v2; - } - *out = v1 & 0xFFFF; -} -/****************************************************/ -parse_arg (out) -long *out; -{ - while (isspace (line[lp])) lp++; -/* while (line[lp]==' ') lp++; */ - parse_sum (out); -} -/****************************************************/ -parse_oct (out) -long *out; -{ - long sign,val; - - while (isspace (line[lp])) lp++; -/* while (line[lp]==' ') lp++; */ - sign = line[lp]; - if (sign=='+' || sign=='-') lp++; - parse_digits (8, &val); - if (sign=='-') val = (-val) & 0xFFFF; - *out =val; -} -/****************************************************/ -parse_dec (out, nbwords) -long *out, *nbwords; -{ - long save,sign,val,a,b; - extern double atof(); - double num; - - while (isspace (line[lp])) lp++; -/* while (line[lp]==' ') lp++; */ - save = lp; - sign = line[lp]; - if (sign=='+' || sign=='-') lp++; - parse_digits (10, &val); - if (sign=='-') val = (-val) & 0xFFFF; - out[0] = val; - *nbwords = 1; - - if (line[lp]=='.' || toupper(line[lp])=='E') { - num = atof( &(line[save]) ); - while (isdigit(line[lp]) || line[lp]=='.' || toupper(line[lp])=='E') lp++; - double_to_hp (num, &a, &b); - out[0] = a; - out[1] = b; - *nbwords = 2; - } -} -/****************************************************/ -mem_group (opcode, code) -int opcode; -long *code; -{ - long arg, out; - parse_arg (&arg); - if (arg < 02000) { - out = opcode + arg; - } else if ((arg & 0776000) == (addr & 0776000)) { - out = opcode + 02000 + (arg & 01777); - } else { - err ("Operand page error"); - out = opcode; - } - if (line[lp]==',' && toupper(line[lp+1])=='I') { - out = out + 0100000L; - lp += 2; - } - code[0] = out; -} -/****************************************************/ -io_group (opcode, code) -int opcode; -long *code; -{ - long arg, out; - parse_arg (&arg); - if (arg > 077) { - err ("Select code > 77B"); - arg = 0; - } - out = opcode + arg; - if (line[lp]==',' && toupper(line[lp+1])=='C') { - out += 001000; - lp += 2; - } - code[0] = out; -} -/****************************************************/ -overflow_group (opcode, code) -int opcode; -long *code; -{ - code[0] = opcode; - if (isspace (line[lp]) && toupper(line[lp+1])=='C') { -/* if (line[lp]==' ' && line[lp+1]=='C') { */ - code[0] += 001000; - lp += 2; - } -} -/****************************************************/ -parse_label (label) -char *label; -{ - int i=0; - while (is_valid_label_char(line[lp])) { - if (i0 && line[lp-1]=='\n') line[lp-1]=0; - print_flag = 0; - count = rep_count; - if (!count) count=1; - rep_count=0; - if (line[0]=='*') { - if (pass==2) { - fprintf (listfile, "%s\n", line); - print_flag = 1; - } - } else - while (count > 0) { - count--; - lp=0; - parse_label (label); - while (isspace (line[lp])) lp++; -/* while (line[lp]==' ') lp++; */ - if (try("IFN")) { - if (!ifn_flag) xif_flag=1; - } else if (try("IFZ")) { - if (!ifz_flag) xif_flag=1; - } else if (try("XIF")) { - xif_flag=0; - } else if (xif_flag) { - while (line[lp]) lp++; - } else if (try("ORG")) { - parse_arg (&arg); - addr = arg; - if (pass==2) { - fprintf (listfile, " %05lo %s\n", addr, line); - print_flag = 1; - if (strlen(label)) err ("Unexpected label"); - } - } else if (try("EQU")) { - parse_arg (&arg); - if (pass==1) { - if (strlen(label)) insert_label (label, arg); - } else { - fprintf (listfile, " %05lo %s\n", arg, line); - print_flag = 1; - if (!strlen(label)) err ("Label required"); - } - } else if (!isalpha(line[lp])) { - if (pass==2) err ("No instruction on line"); - } else { - - if (pass==1 && strlen(label)) insert_label (label,addr); - - if (try("HED") || try("SUP") || try("SPC") || - try("SKP") || try("UNS") || try("UNL") || - try("LST")) { - - /* Listing control ops are ignored */ - - } else if (try("END")) { - - /* No action required */ - - } else if (try("REP")) { - parse_arg (&arg); - if (arg < 1 || arg > 9999) { - err ("Illegal repeat count"); - } else { - rep_count = arg; - } - } else if (try("ABS")) { - if (pass==1) addr++; - else { - parse_arg (code); - emit (code[0]); - } - } else if (try("DEF")) { - if (pass==1) addr++; - else { - parse_arg (code); - if (line[lp]==',' && toupper(line[lp+1])=='I') { - code[0] |= 0x8000; - lp += 2; - } - emit (code[0]); - } - } else if (try("DEC")) { - long nbwords; - parse_dec (code, &nbwords); - if (pass==1) addr += nbwords; - else { - if (nbwords > 0) emit (code[0]); - if (nbwords > 1) emit (code[1]); - } - } else if (try("OCT")) { - while (1) { - parse_oct (code); - if (pass==1) addr++; - else emit (code[0]); - if (line[lp]==',') lp++; - else break; - } - } else if (try("ASC")) { - long nbwords,c1,c2; - parse_arg (&nbwords); - if (pass==1) addr += nbwords; - else if (line[lp++]!=',') err ("Missing comma"); - else - while (nbwords>0) { - if (line[lp]>31) c1=line[lp++]; else c1=' '; - if (line[lp]>31) c2=line[lp++]; else c2=' '; - emit (c1*256 + c2); - nbwords--; - } - } else if (try("BSS")) { - long nbwords; - if (pass==2) { - fprintf (listfile, " %05lo %s\n", - addr, line); - print_flag = 1; - } - parse_arg (&nbwords); - addr += nbwords; - } - else if (pass==1) addr++; - else if (try_mem_group(code)) emit (code[0]); - else if (try_srg_a(code)) emit (code[0]); - else if (try_srg_b(code)) emit (code[0]); - else if (try_asg_a(code)) emit (code[0]); - else if (try_asg_b(code)) emit (code[0]); - else if (try_io_group(code)) emit (code[0]); - else if (try_overflow(code)) emit (code[0]); - else { - emit (0L); - err ("Unknown instruction"); - } - } - if (pass==2 && !end_of_line()) err ("Missing End-of-line"); - if (pass==2 && !print_flag) fprintf (listfile, " %12s %s\n", - " ", line); - } - } - } -} -/****************************************************/ -main (argc,argv) -int argc; -char *argv[]; -{ - FILE *f1; - int ii,jj; - - jj=1; - for (ii=1 ; ii < argc ; ii++) { - if (!strcmp(argv[ii],"-n")) ifn_flag=1; - else if (!strcmp(argv[ii],"-z")) ifz_flag=1; - else argv[jj++] = argv[ii]; - } - argc = jj; - - if (argc < 2) { - printf ("Usage: hpasm options filename...\n"); - return (-1); - } - - start_output (argv[1]); - start_listing (argv[1]); - - printf ("<< PASS 1 >>\n"); - nbsyms=0; - err_count=0; - pass=1; - - for (ii=1 ; ii < argc; ii++) { - f1 = fopen (argv[ii], "r"); - if (!f1) { - printf ("File '%s' not found!\n\n", argv[ii]); - } else { - line_count=0; - printf ("%s ", argv[ii]); - fflush (stdout); - asm_pass (f1); - fclose (f1); - printf ("%d lines\n", line_count); - } - } - - show_labels(); - - printf ("<< PASS 2 >>\n"); - pass=2; - - for (ii=1 ; ii < argc; ii++) { - f1 = fopen (argv[ii], "r"); - if (!f1) { - printf ("File '%s' not found!\n\n", argv[ii]); - } else { - line_count=0; - printf ("%s ", argv[ii]); - fflush (stdout); - asm_pass (f1); - printf ("%d lines\n", line_count); - fclose (f1); - } - } - - fprintf (listfile, "%ld ERRORS\n", err_count); - printf ("%ld ERRORS\n", err_count); - - finish_output(); - finish_listing(); - - return (0); -} -/****************************************************/ +/* +** HPASM.C -- Assembler for HP2100 +*/ +/****************************************************/ +#include +#include +#include +/****************************************************/ +#define MXSYM 1500 +#define MXLABEL 5 +#define ALLOW_SPECIALS_IN_LABEL 1 +/****************************************************/ +typedef struct { + char name[MXLABEL+1]; + long value; +} SYM; +/****************************************************/ +long nbsyms; +SYM symtab[MXSYM]; +long pass; +long addr; +char line[80]; +long lp; +long line_count; +long err_count; +long print_flag; /* =1 if current line has been printed */ +int ifn_flag=0; /* =1 if IFN does not skip */ +int ifz_flag=0; /* =1 if IFZ does not skip */ +int xif_flag=0; /* =1 if we are skipping until XIF */ +int rep_count=0; /* >0 if REP statement in process */ +/****************************************************/ +/* +** Variables for listing output routines +*/ +char listfilename[256]; +FILE *listfile; +/****************************************************/ +start_listing (nam) +/* +** Initialize listing output file +*/ +char *nam; +{ + char *ppos; + + strncpy (listfilename, nam, 250); + if (ppos = strrchr (listfilename, '.')) strcpy (ppos, ".lst"); + else strcat (listfilename, ".lst"); + listfile = fopen (listfilename, "w"); +} +/****************************************************/ +finish_listing () { +/* +** Finish off listing output file +*/ + fclose (listfile); + printf ("Listing is in %s\n", listfilename); +} +/****************************************************/ +/* +** Variables for binary output routines +*/ +char outfilename[256]; +FILE *outfile; +long outaddr; +long outcount; +long outbufsize=27; +long outbuf[27]; +/****************************************************/ +start_output (nam) +/* +** Initialize binary output file +*/ +char *nam; +{ + char *ppos; + + strncpy (outfilename, nam, 250); + if (ppos = strrchr (outfilename, '.')) strcpy (ppos, ".bin"); + else strcat (outfilename, ".bin"); + outfile = fopen (outfilename, "wb"); + outaddr = 0; + outcount = 0; +} +/****************************************************/ +send_output () { +/* +** Write block to binary output file +*/ + long ii,sum; + + fputc (outcount, outfile); + fputc (0, outfile); + fputc (outaddr >> 8, outfile); + fputc (outaddr & 255, outfile); + sum = outaddr; + for (ii=0 ; ii < outcount ; ii++) { + fputc (outbuf[ii] >> 8, outfile); + fputc (outbuf[ii] & 255, outfile); + sum += outbuf[ii]; + } + fputc (sum >> 8, outfile); + fputc (sum & 255, outfile); + outcount = 0; +} +/****************************************************/ +output_word (addr,word) +/* +** Write word to binary output file +*/ +long addr; +long word; +{ + if (outcount && (outaddr + outcount) != addr) { + send_output(); + } + if (!outcount) outaddr = addr; + outbuf[outcount++] = word; + if (outcount == outbufsize) { + send_output(); + } +} +/****************************************************/ +finish_output () { +/* +** Finish current output block, write trailing leader, +** and close output file +*/ + int ii; + if (outcount) send_output(); + for (ii=0; ii<20; ii++) fputc (0, outfile); + fclose (outfile); + printf ("Output is in %s\n", outfilename); +} +/****************************************************/ +emit (code) +long code; +{ + fprintf (listfile, " %05lo %06lo ", addr, code); + if (!print_flag) { + fprintf (listfile, "%s", line); + print_flag = 1; + } + fprintf (listfile, "\n"); + + output_word (addr, code); + addr++; +} +/****************************************************/ +err (text) +char *text; +{ + if (!print_flag) { + fprintf (listfile, " %12s %s\n", " ", line); + print_flag = 1; + } + fprintf (listfile, "ERROR: %s\n", text); + err_count++; +} +/****************************************************/ +int is_valid_label_char (ch) +char ch; +{ + if (isalpha(ch)) return (1); + if (isdigit(ch)) return (1); + if (ch=='.') return (1); +#if ALLOW_SPECIALS_IN_LABEL + if (ch=='&') return (1); + if (ch=='?') return (1); + if (ch=='#') return (1); + if (ch=='/') return (1); + if (ch=='%') return (1); + if (ch=='$') return (1); + if (ch=='[') return (1); + if (ch=='@') return (1); + if (ch=='!') return (1); + if (ch=='^') return (1); +#endif + return (0); +} +/****************************************************/ +void insert_label (label, value) +char *label; +long value; +/* +** Insert label into symbol table +*/ +{ + long i; + if (strlen(label)>MXLABEL) { + err ("Symbol name > 5"); + return; + } + for (i=0; i= 1.0) { num = num / 2; exp++; } + while (num < 0.5) { num = num * 2; exp--; } + + if (neg) num = -num; + + if (neg) num += (0177 / 65536.0 / 65536.0); + else num += (0200 / 65536.0 / 65536.0); + + if (neg && num >= -0.5) { num = num * 2; exp--; } + if (!neg && num >= 1.0) { num = num / 2; exp++; } + + if (exp < -128) return (-2); + + if (exp > 127) { + if (neg) { *a = 0x8000; *b = 0x01FE; } + else { *a = 0x7FFF; *b = 0xFFFE; } + return (-3); + } + + frac = num * 65536.0 * 128.0 * 256.0; + exp = exp << 1; + if (exp < 0) exp++; + *a = (frac >> 16) & 0xFFFF; + *b = (frac & 0xFF00) | (exp & 0x00FF); + + return (0); +} +/****************************************************/ +parse_digits (base, out) +int base; +long *out; +{ + long dig, val; + val = 0; + dig = line[lp] - '0'; + while (dig>=0 && digMXLABEL) { + err ("Illegal symbol"); + *out=0; + } else { + sym[count]=0; + find_label (sym, out); + } +} +/****************************************************/ +parse_term (out) +long *out; +{ + char ch; + ch = line[lp]; + if (isdigit(ch)) parse_const (out); + else if (is_valid_label_char(ch)) parse_sym (out); + else if (ch=='*') { lp++; *out = addr; } + else { + err ("Syntax error in expression"); + *out=0; + } +} +/****************************************************/ +parse_neg (out) +long *out; +{ + long temp; + if (line[lp]=='-') { + lp++; + parse_term (&temp); + *out = (-temp) & 0xFFFF; + } else if (line[lp]=='+') { + lp++; + parse_term (out); + } else { + parse_term (out); + } +} +/****************************************************/ +parse_sum (out) +long *out; +{ + long v1,v2,op; + parse_neg (&v1); + while (line[lp]=='+' || line[lp]=='-') { + op = line[lp++]; + parse_neg (&v2); + if (op=='+') v1 = v1 + v2; + if (op=='-') v1 = v1 - v2; + } + *out = v1 & 0xFFFF; +} +/****************************************************/ +parse_arg (out) +long *out; +{ + while (isspace (line[lp])) lp++; +/* while (line[lp]==' ') lp++; */ + parse_sum (out); +} +/****************************************************/ +parse_oct (out) +long *out; +{ + long sign,val; + + while (isspace (line[lp])) lp++; +/* while (line[lp]==' ') lp++; */ + sign = line[lp]; + if (sign=='+' || sign=='-') lp++; + parse_digits (8, &val); + if (sign=='-') val = (-val) & 0xFFFF; + *out =val; +} +/****************************************************/ +parse_dec (out, nbwords) +long *out, *nbwords; +{ + long save,sign,val,a,b; + extern double atof(); + double num; + + while (isspace (line[lp])) lp++; +/* while (line[lp]==' ') lp++; */ + save = lp; + sign = line[lp]; + if (sign=='+' || sign=='-') lp++; + parse_digits (10, &val); + if (sign=='-') val = (-val) & 0xFFFF; + out[0] = val; + *nbwords = 1; + + if (line[lp]=='.' || toupper(line[lp])=='E') { + num = atof( &(line[save]) ); + while (isdigit(line[lp]) || line[lp]=='.' || toupper(line[lp])=='E') lp++; + double_to_hp (num, &a, &b); + out[0] = a; + out[1] = b; + *nbwords = 2; + } +} +/****************************************************/ +mem_group (opcode, code) +int opcode; +long *code; +{ + long arg, out; + parse_arg (&arg); + if (arg < 02000) { + out = opcode + arg; + } else if ((arg & 0776000) == (addr & 0776000)) { + out = opcode + 02000 + (arg & 01777); + } else { + err ("Operand page error"); + out = opcode; + } + if (line[lp]==',' && toupper(line[lp+1])=='I') { + out = out + 0100000L; + lp += 2; + } + code[0] = out; +} +/****************************************************/ +io_group (opcode, code) +int opcode; +long *code; +{ + long arg, out; + parse_arg (&arg); + if (arg > 077) { + err ("Select code > 77B"); + arg = 0; + } + out = opcode + arg; + if (line[lp]==',' && toupper(line[lp+1])=='C') { + out += 001000; + lp += 2; + } + code[0] = out; +} +/****************************************************/ +overflow_group (opcode, code) +int opcode; +long *code; +{ + code[0] = opcode; + if (isspace (line[lp]) && toupper(line[lp+1])=='C') { +/* if (line[lp]==' ' && line[lp+1]=='C') { */ + code[0] += 001000; + lp += 2; + } +} +/****************************************************/ +parse_label (label) +char *label; +{ + int i=0; + while (is_valid_label_char(line[lp])) { + if (i0 && line[lp-1]=='\n') line[lp-1]=0; + print_flag = 0; + count = rep_count; + if (!count) count=1; + rep_count=0; + if (line[0]=='*') { + if (pass==2) { + fprintf (listfile, "%s\n", line); + print_flag = 1; + } + } else + while (count > 0) { + count--; + lp=0; + parse_label (label); + while (isspace (line[lp])) lp++; +/* while (line[lp]==' ') lp++; */ + if (try("IFN")) { + if (!ifn_flag) xif_flag=1; + } else if (try("IFZ")) { + if (!ifz_flag) xif_flag=1; + } else if (try("XIF")) { + xif_flag=0; + } else if (xif_flag) { + while (line[lp]) lp++; + } else if (try("ORG")) { + parse_arg (&arg); + addr = arg; + if (pass==2) { + fprintf (listfile, " %05lo %s\n", addr, line); + print_flag = 1; + if (strlen(label)) err ("Unexpected label"); + } + } else if (try("EQU")) { + parse_arg (&arg); + if (pass==1) { + if (strlen(label)) insert_label (label, arg); + } else { + fprintf (listfile, " %05lo %s\n", arg, line); + print_flag = 1; + if (!strlen(label)) err ("Label required"); + } + } else if (!isalpha(line[lp])) { + if (pass==2) err ("No instruction on line"); + } else { + + if (pass==1 && strlen(label)) insert_label (label,addr); + + if (try("HED") || try("SUP") || try("SPC") || + try("SKP") || try("UNS") || try("UNL") || + try("LST")) { + + /* Listing control ops are ignored */ + + } else if (try("END")) { + + /* No action required */ + + } else if (try("REP")) { + parse_arg (&arg); + if (arg < 1 || arg > 9999) { + err ("Illegal repeat count"); + } else { + rep_count = arg; + } + } else if (try("ABS")) { + if (pass==1) addr++; + else { + parse_arg (code); + emit (code[0]); + } + } else if (try("DEF")) { + if (pass==1) addr++; + else { + parse_arg (code); + if (line[lp]==',' && toupper(line[lp+1])=='I') { + code[0] |= 0x8000; + lp += 2; + } + emit (code[0]); + } + } else if (try("DEC")) { + long nbwords; + parse_dec (code, &nbwords); + if (pass==1) addr += nbwords; + else { + if (nbwords > 0) emit (code[0]); + if (nbwords > 1) emit (code[1]); + } + } else if (try("OCT")) { + while (1) { + parse_oct (code); + if (pass==1) addr++; + else emit (code[0]); + if (line[lp]==',') lp++; + else break; + } + } else if (try("ASC")) { + long nbwords,c1,c2; + parse_arg (&nbwords); + if (pass==1) addr += nbwords; + else if (line[lp++]!=',') err ("Missing comma"); + else + while (nbwords>0) { + if (line[lp]>31) c1=line[lp++]; else c1=' '; + if (line[lp]>31) c2=line[lp++]; else c2=' '; + emit (c1*256 + c2); + nbwords--; + } + } else if (try("BSS")) { + long nbwords; + if (pass==2) { + fprintf (listfile, " %05lo %s\n", + addr, line); + print_flag = 1; + } + parse_arg (&nbwords); + addr += nbwords; + } + else if (pass==1) addr++; + else if (try_mem_group(code)) emit (code[0]); + else if (try_srg_a(code)) emit (code[0]); + else if (try_srg_b(code)) emit (code[0]); + else if (try_asg_a(code)) emit (code[0]); + else if (try_asg_b(code)) emit (code[0]); + else if (try_io_group(code)) emit (code[0]); + else if (try_overflow(code)) emit (code[0]); + else { + emit (0L); + err ("Unknown instruction"); + } + } + if (pass==2 && !end_of_line()) err ("Missing End-of-line"); + if (pass==2 && !print_flag) fprintf (listfile, " %12s %s\n", + " ", line); + } + } + } +} +/****************************************************/ +main (argc,argv) +int argc; +char *argv[]; +{ + FILE *f1; + int ii,jj; + + jj=1; + for (ii=1 ; ii < argc ; ii++) { + if (!strcmp(argv[ii],"-n")) ifn_flag=1; + else if (!strcmp(argv[ii],"-z")) ifz_flag=1; + else argv[jj++] = argv[ii]; + } + argc = jj; + + if (argc < 2) { + printf ("Usage: hpasm options filename...\n"); + return (-1); + } + + start_output (argv[1]); + start_listing (argv[1]); + + printf ("<< PASS 1 >>\n"); + nbsyms=0; + err_count=0; + pass=1; + + for (ii=1 ; ii < argc; ii++) { + f1 = fopen (argv[ii], "r"); + if (!f1) { + printf ("File '%s' not found!\n\n", argv[ii]); + } else { + line_count=0; + printf ("%s ", argv[ii]); + fflush (stdout); + asm_pass (f1); + fclose (f1); + printf ("%d lines\n", line_count); + } + } + + show_labels(); + + printf ("<< PASS 2 >>\n"); + pass=2; + + for (ii=1 ; ii < argc; ii++) { + f1 = fopen (argv[ii], "r"); + if (!f1) { + printf ("File '%s' not found!\n\n", argv[ii]); + } else { + line_count=0; + printf ("%s ", argv[ii]); + fflush (stdout); + asm_pass (f1); + printf ("%d lines\n", line_count); + fclose (f1); + } + } + + fprintf (listfile, "%ld ERRORS\n", err_count); + printf ("%ld ERRORS\n", err_count); + + finish_output(); + finish_listing(); + + return (0); +} +/****************************************************/ diff --git a/crossassemblers/macro1/macro1.c b/crossassemblers/macro1/macro1.c index a3d6799..4ccb540 100644 --- a/crossassemblers/macro1/macro1.c +++ b/crossassemblers/macro1/macro1.c @@ -1,3068 +1,3068 @@ -/* - * $Id: macro1.c,v 1.74 2003/10/23 23:29:17 phil Exp $ - * - * TODO: - * have flex() use nextfiodec()?? (what if legal in repeat???) - * "flexx" should give right justified result??? - * squawk if nextfiodec called in a repeat w/ a delim? - * - * forbid variables/constants in macros - * forbid text in repeat?? - * forbid start in repeat or macro - * use same error TLA's as MACRO??? - * IPA error for overbar on LHS of = - * variables returns value?? - * - * macro addressing: labels defined during macro are local use only???? - * spacewar expects this??? (is it wrong?) - * - * self-feeding lines: \n legal anywhere \t is - * read next token into "token" buffer -- avoid saving "line"? - * remove crocks from "define" - * list title (first line of file) should not be parsed as source? - * incorrect listing for bare "start" - * list only 4 digits for address column - * - * other; - * note variables in symbol dump, xref? - * no "permenant" symbols; flush -p? rename .ini? - * keep seperate macro/pseudo table? - * verify bad input(?) detected - * implement dimension pseudo? - * remove crocks for '/' and ','? - */ - -/* - * Program: MACRO1 - * File: macro1.c - * Author: Gary A. Messenbrink (macro8) - * MACRO7 modifications: Bob Supnik - * MACRO1 modifications: Bob Supnik - * slashed to be more like real MACRO like by Phil Budne - * - * Purpose: A 2 pass PDP-1 assembler - * - * NAME - * macro1 - a PDP-1 assembler. - * - * SYNOPSIS: - * macro1 [ -d -p -m -r -s -x ] inputfile inputfile... - * - * DESCRIPTION - * This is a cross-assembler to for PDP-1 assembly language programs. - * It will produce an output file in rim format only. - * A listing file is always produced and with an optional symbol table - * and/or a symbol cross-reference (concordance). The permanent symbol - * table can be output in a form that may be read back in so a customized - * permanent symbol table can be produced. Any detected errors are output - * to a separate file giving the filename in which they were detected - * along with the line number, column number and error message as well as - * marking the error in the listing file. - * The following file name extensions are used: - * .mac source code (input) - * .lst assembly listing (output) - * .rim assembly output in DEC's rim format (output) - * .prm permanent symbol table in form suitable for reading after - * the EXPUNGE pseudo-op. - * .sym "symbol punch" tape (for DDT, or reloading into macro) - * - * OPTIONS - * -d Dump the symbol table at end of assembly - * -p Generate a file with the permanent symbols in it. - * (To get the current symbol table, assemble a file than has only - * START in it.) - * -x Generate a cross-reference (concordance) of user symbols. - * -r Output a tape using only RIM format (else output block loader) - * -s Output a symbol dump tape (loader + loader blocks) - * -S file - * Read a symbol tape back in - * - * DIAGNOSTICS - * Assembler error diagnostics are output to an error file and inserted - * in the listing file. Each line in the error file has the form - * - * :: : error: at Loc = - * - * An example error message is: - * - * bintst.7:17:9 : error: undefined symbol "UNDEF" at Loc = 07616 - * - * The error diagnostics put in the listing start with a two character - * error code (if appropriate) and a short message. A carat '^' is - * placed under the item in error if appropriate. - * An example error message is: - * - * 17 07616 3000 DAC UNDEF - * UD undefined ^ - * 18 07617 1777 TAD I DUMMY - * - * Undefined symbols are marked in the symbol table listing by prepending - * a '?' to the symbol. Redefined symbols are marked in the symbol table - * listing by prepending a '#' to the symbol. Examples are: - * - * #REDEF 04567 - * SWITCH 07612 - * ?UNDEF 00000 - * - * Refer to the code for the diagnostic messages generated. - * - * REFERENCES: - * This assembler is based on the pal assember by: - * Douglas Jones and - * Rich Coon - * - * COPYRIGHT NOTICE: - * This is free software. There is no fee for using it. You may make - * any changes that you wish and also give it away. If you can make - * a commercial product out of it, fine, but do not put any limits on - * the purchaser's right to do the same. If you improve it or fix any - * bugs, it would be nice if you told me and offered me a copy of the - * new version. - * - * - * Amendments Record: - * Version Date by Comments - * ------- ------- --- --------------------------------------------------- - * v1.0 12Apr96 GAM Original - * v1.1 18Nov96 GAM Permanent symbol table initialization error. - * v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. - * v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). - * v1.4 29Nov96 GAM Fixed bug in checksum generation. - * v2.1 08Dec96 GAM Added concordance processing (cross reference). - * v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). - * v2.3 2Feb97 GAM Fixed paging problem in cross reference output. - * v3.0 14Feb97 RMS MACRO8X features. - * 8Mar97 RMS MACRO7 released w/ sim8swre - * 13Mar97 RMS MACRO1 released w/ lispswre - * 28Nov01 RMS MACRO1 released w/ simtools - * 5Mar03 DP MACRO1 released w/ ddt1 - * 2003 PLB major reworking, assembles MACRO, DDT - */ - - -#include -#include -#include -#include - -#define LINELEN 96 -#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ -#define NAMELEN 128 -#define SYMBOL_COLUMNS 5 -#define SYMLEN 7 -/*#define SYMSIG 4 /* EXP: significant chars in a symbol */ -#define SYMBOL_TABLE_SIZE 8192 -#define MAC_MAX_ARGS 20 -#define MAC_MAX_LENGTH 8192 -#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ - -#define MAX_LITERALS 1000 -#define MAX_CONSTANTS 10 /* max number of "constants" blocks */ - -#define XREF_COLUMNS 8 - -#define ADDRESS_FIELD 0007777 -#define INDIRECT_BIT 0010000 -#define OP_CODE 0760000 - -#define CONCISE_LC 072 -#define CONCISE_UC 074 - -/* Macro to get the number of elements in an array. */ -#define DIM(a) (sizeof(a)/sizeof(a[0])) - -#define ISBLANK(c) ((c==' ') || (c=='\f')) -#define ISEND(c) ((c=='\0')|| (c=='\n') || (c == '\t')) -#define ISDONE(c) ((c=='/') || ISEND(c)) - -#define ISOVERBAR(c) (c == '\\' || c == '~') - -/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ -/* (true) if the stated condtion is met. */ -/* Use these to test attributes. The proper bits are extracted and then */ -/* tested. */ -#define M_DEFINED(s) (((s) & DEFINED) == DEFINED) -#define M_DUPLICATE(s) (((s) & DUPLICATE) == DUPLICATE) -#define M_FIXED(s) (((s) & FIXED) == FIXED) -#define M_LABEL(s) (((s) & LABEL) == LABEL) -#define M_PSEUDO(s) (((s) & PSEUDO) == PSEUDO) -#define M_EPSEUDO(s) (((s) & EPSEUDO) == EPSEUDO) -#define M_MACRO(s) (((s) & MACRO) == MACRO) -#define M_NOTRDEF(s) (((s) & NOTRDEF) != 0) - -typedef unsigned char BOOL; -typedef unsigned char BYTE; -typedef int WORD32; - -#ifndef FALSE - #define FALSE 0 - #define TRUE (!FALSE) -#endif - -/* Line listing styles. Used to control listing of lines. */ -enum linestyle_t -{ - LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL, LINE_LOC -}; -typedef enum linestyle_t LINESTYLE_T; - -/* Symbol Types. */ -/* Note that the names that have FIX as the suffix contain the FIXED bit */ -/* included in the value. */ -enum symtyp -{ - UNDEFINED = 0000, - DEFINED = 0001, - FIXED = 0002, - LABEL = 0010 | DEFINED, - REDEFINED = 0020 | DEFINED, - DUPLICATE = 0040 | DEFINED, - PSEUDO = 0100 | FIXED | DEFINED, - EPSEUDO = 0200 | FIXED | DEFINED, - MACRO = 0400 | DEFINED, - DEFFIX = DEFINED | FIXED, - NOTRDEF = (MACRO | PSEUDO | LABEL | FIXED) & ~DEFINED -}; -typedef enum symtyp SYMTYP; - -enum pseudo_t { - DECIMAL, - DEFINE, - FLEX, - CONSTANTS, - OCTAL, - REPEAT, - START, - CHAR, - VARIABLES, - TEXT, - NOINPUT, - EXPUNGE -}; -typedef enum pseudo_t PSEUDO_T; - -struct sym_t -{ - SYMTYP type; - char name[SYMLEN]; - WORD32 val; - WORD32 xref_index; - WORD32 xref_count; -}; -typedef struct sym_t SYM_T; - -struct emsg_t -{ - char *list; - char *file; -}; -typedef struct emsg_t EMSG_T; - -struct errsave_t -{ - char *mesg; - WORD32 col; -}; -typedef struct errsave_t ERRSAVE_T; - -/*----------------------------------------------------------------------------*/ - -/* Function Prototypes */ - -int binarySearch( char *name, int start, int symbol_count ); -int compareSymbols( const void *a, const void *b ); -SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); -void errorLexeme( EMSG_T *mesg, WORD32 col ); -void errorMessage( EMSG_T *mesg, WORD32 col ); -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); -SYM_T eval( void ); -SYM_T *evalSymbol( void ); -void getArgs( int argc, char *argv[] ); -SYM_T getExpr( void ); -WORD32 getExprs( void ); -WORD32 incrementClc( void ); -WORD32 literal( WORD32 value ); -BOOL isLexSymbol(); -char *lexemeToName( char *name, WORD32 from, WORD32 term ); -void listLine( void ); -SYM_T *lookup( char *name, int type ); -void moveToEndOfLine( void ); -void next(int); -void onePass( void ); -void printCrossReference( void ); -void printErrorMessages( void ); -void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); -void printPageBreak( void ); -void printPermanentSymbolTable( void ); -void printSymbolTable( void ); -BOOL pseudo( PSEUDO_T val ); -void punchLocObject( WORD32 loc, WORD32 val ); -void punchOutObject( WORD32 loc, WORD32 val ); -void punchLeader( WORD32 count ); -void punchLoader( void ); -void flushLoader( void ); -void readLine( void ); -void saveError( char *mesg, WORD32 cc ); -void topOfForm( char *title, char *sub_title ); -void constants(void); -void variables(void); -void eob(void); -void dump_symbols(void); - -/*----------------------------------------------------------------------------*/ - -/* Table of pseudo-ops (directives) which are used to setup the symbol */ -/* table on startup */ -SYM_T pseudos[] = -{ - { PSEUDO, "consta", CONSTANTS }, - { PSEUDO, "define", DEFINE }, /* Define macro. */ - { PSEUDO, "repeat", REPEAT }, - { PSEUDO, "start", START }, /* Set starting address. */ - { PSEUDO, "variab", VARIABLES }, - { PSEUDO, "text", TEXT }, - { PSEUDO, "noinpu", NOINPUT }, - { PSEUDO, "expung", EXPUNGE }, -/* the following can appear in expressions: */ - { EPSEUDO, "charac", CHAR }, - { EPSEUDO, "decima", DECIMAL }, /* base 10. */ - { EPSEUDO, "flexo", FLEX }, - { EPSEUDO, "octal", OCTAL }, /* Read literal constants in base 8. */ -}; - -/* Symbol Table */ -/* The table is put in lexical order on startup, so symbols can be */ -/* inserted as desired into the initial table. */ -#define DIO 0320000 -#define JMP 0600000 -SYM_T permanent_symbols[] = -{ - /* Memory Reference Instructions */ - { DEFFIX, "and", 0020000 }, - { DEFFIX, "ior", 0040000 }, - { DEFFIX, "xor", 0060000 }, - { DEFFIX, "xct", 0100000 }, - { DEFFIX, "lac", 0200000 }, - { DEFFIX, "lio", 0220000 }, - { DEFFIX, "dac", 0240000 }, - { DEFFIX, "dap", 0260000 }, - { DEFFIX, "dip", 0300000 }, - { DEFFIX, "dio", 0320000 }, - { DEFFIX, "dzm", 0340000 }, - { DEFFIX, "add", 0400000 }, - { DEFFIX, "sub", 0420000 }, - { DEFFIX, "idx", 0440000 }, - { DEFFIX, "isp", 0460000 }, - { DEFFIX, "sad", 0500000 }, - { DEFFIX, "sas", 0520000 }, - { DEFFIX, "mul", 0540000 }, - { DEFFIX, "mus", 0540000 }, /* for spacewar */ - { DEFFIX, "div", 0560000 }, - { DEFFIX, "dis", 0560000 }, /* for spacewar */ - { DEFFIX, "jmp", 0600000 }, - { DEFFIX, "jsp", 0620000 }, - { DEFFIX, "skip", 0640000 }, /* for spacewar */ - { DEFFIX, "cal", 0160000 }, - { DEFFIX, "jda", 0170000 }, - { DEFFIX, "i", 0010000 }, - { DEFFIX, "skp", 0640000 }, - { DEFFIX, "law", 0700000 }, - { DEFFIX, "iot", 0720000 }, - { DEFFIX, "opr", 0760000 }, - { DEFFIX, "nop", 0760000 }, - /* Shift Instructions */ - { DEFFIX, "ral", 0661000 }, - { DEFFIX, "ril", 0662000 }, - { DEFFIX, "rcl", 0663000 }, - { DEFFIX, "sal", 0665000 }, - { DEFFIX, "sil", 0666000 }, - { DEFFIX, "scl", 0667000 }, - { DEFFIX, "rar", 0671000 }, - { DEFFIX, "rir", 0672000 }, - { DEFFIX, "rcr", 0673000 }, - { DEFFIX, "sar", 0675000 }, - { DEFFIX, "sir", 0676000 }, - { DEFFIX, "scr", 0677000 }, - { DEFFIX, "1s", 0000001 }, - { DEFFIX, "2s", 0000003 }, - { DEFFIX, "3s", 0000007 }, - { DEFFIX, "4s", 0000017 }, - { DEFFIX, "5s", 0000037 }, - { DEFFIX, "6s", 0000077 }, - { DEFFIX, "7s", 0000177 }, - { DEFFIX, "8s", 0000377 }, - { DEFFIX, "9s", 0000777 }, - /* Skip Microinstructions */ - { DEFFIX, "sza", 0640100 }, - { DEFFIX, "spa", 0640200 }, - { DEFFIX, "sma", 0640400 }, - { DEFFIX, "szo", 0641000 }, - { DEFFIX, "spi", 0642000 }, - { DEFFIX, "szs", 0640000 }, - { DEFFIX, "szf", 0640000 }, - /*{ DEFFIX, "clo", 0651600 },*/ - - /* Operate Microinstructions */ - { DEFFIX, "clf", 0760000 }, - { DEFFIX, "stf", 0760010 }, - { DEFFIX, "cla", 0760200 }, - /*{ DEFFIX, "lap", 0760300 },*/ - { DEFFIX, "hlt", 0760400 }, - { DEFFIX, "xx", 0760400 }, - { DEFFIX, "cma", 0761000 }, - { DEFFIX, "clc", 0761200 }, - { DEFFIX, "lat", 0762200 }, - { DEFFIX, "cli", 0764000 }, - /* IOT's */ - /*{ DEFFIX, "ioh", 0730000 },*/ - { DEFFIX, "rpa", 0730001 }, - { DEFFIX, "rpb", 0730002 }, - { DEFFIX, "rrb", 0720030 }, - { DEFFIX, "ppa", 0730005 }, - { DEFFIX, "ppb", 0730006 }, - { DEFFIX, "tyo", 0730003 }, - { DEFFIX, "tyi", 0720004 }, - { DEFFIX, "dpy", 0730007 }, /* for spacewar, munching squares! */ - { DEFFIX, "lsm", 0720054 }, - { DEFFIX, "esm", 0720055 }, - { DEFFIX, "cbs", 0720056 }, - { DEFFIX, "lem", 0720074 }, - { DEFFIX, "eem", 0724074 }, - { DEFFIX, "cks", 0720033 }, -}; /* End-of-Symbols for Permanent Symbol Table */ - -/* Global variables */ -SYM_T *symtab; /* Symbol Table */ -int symbol_top; /* Number of entries in symbol table. */ - -#define LOADERBASE 07751 - -/* make it relocatable (DDT expects it at 7751) */ -#define LOADER_IN LOADERBASE -#define LOADER_B (LOADERBASE+06) -#define LOADER_A (LOADERBASE+07) -#define LOADER_CK (LOADERBASE+025) -#define LOADER_EN1 (LOADERBASE+026) - -WORD32 loader[] = { - 0730002, /* in, rpb */ - 0320000+LOADER_A, /* dio a */ - 0100000+LOADER_A, /* xct a */ - 0320000+LOADER_CK, /* dio ck */ - 0730002, /* rpb */ - 0320000+LOADER_EN1, /* dio en1 */ - 0730002, /* b, rpb */ - 0000000, /* a, xx */ - 0210000+LOADER_A, /* lac i a */ - 0400000+LOADER_CK, /* add ck */ - 0240000+LOADER_CK, /* dac ck */ - 0440000+LOADER_A, /* idx a */ - 0520000+LOADER_EN1, /* sas en1 */ - 0600000+LOADER_B, /* jmp b */ - 0200000+LOADER_CK, /* lac ck */ - 0400000+LOADER_EN1, /* add en1 */ - 0730002, /* rpb */ - 0320000+LOADER_CK, /* dio ck */ - 0520000+LOADER_CK, /* sas ck */ - 0760400, /* hlt */ - 0600000+LOADER_IN /* jmp in */ - /* ck, 0 */ - /* en1, 0 */ -}; - -#define LOADERBUFSIZE 0100 /* <=0100, power of 2*/ -#define LOADERBUFMASK (LOADERBUFSIZE-1) /* for block alignment */ - -WORD32 loaderbuf[LOADERBUFSIZE]; -WORD32 loaderbufcount; -WORD32 loaderbufstart; - -/*----------------------------------------------------------------------------*/ - -WORD32 *xreftab; /* Start of the concordance table. */ - -ERRSAVE_T error_list[20]; -int save_error_count; - -char s_detected[] = "detected"; -char s_error[] = "error"; -char s_errors[] = "errors"; -char s_no[] = "No"; -char s_page[] = "Page"; -char s_symtable[] = "Symbol Table"; -char s_xref[] = "Cross Reference"; - -/* Assembler diagnostic messages. */ -/* Some attempt has been made to keep continuity with the PAL-III and */ -/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ -/* exists, then the indicator is put in the listing as the first two */ -/* characters of the diagnostic message. The PAL-III indicators where used */ -/* when there was a choice between using MACRO-8 and PAL-III indicators. */ -/* The character pairs and their meanings are: */ -/* DT Duplicate Tag (symbol) */ -/* IC Illegal Character */ -/* ID Illegal Redefinition of a symbol. An attempt was made to give */ -/* a symbol a new value not via =. */ -/* IE Illegal Equals An equal sign was used in the wrong context, */ -/* (e.g., A+B=C, or TAD A+=B) */ -/* II Illegal Indirect An off page reference was made, but a literal */ -/* could not be generated because the indirect bit was already set. */ -/* IR Illegal Reference (address is not on current page or page zero) */ -/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ -/* RD ReDefintion of a symbol */ -/* ST Symbol Table full */ -/* UA Undefined Address (undefined symbol) */ -/* VR Value Required */ -/* ZE Zero Page Exceeded (see above, or out of space) */ -EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; -EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; -EMSG_T illegal_character = { "IC illegal char", "illegal character" }; -EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; -EMSG_T label_syntax = { "IC label syntax", "label syntax" }; -EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; -EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; -EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; -EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; -EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; -EMSG_T illegal_reference = { "IR off page", "illegal reference" }; -EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; -EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; -EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; -EMSG_T value_required = { "VR value required", "value required" }; -EMSG_T literal_gen_off = { "lit generation off", - "literal generation disabled" }; -EMSG_T literal_overflow = { "PE page exceeded", - "current page literal capacity exceeded" }; -EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; -EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; -EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; -EMSG_T illegal_vfd_value = { "width out of range", - "VFD field width not in range" }; -EMSG_T no_literal_value = { "no value", "No literal value" }; -EMSG_T text_string = { "no delimiter", - "Text string delimiters not matched" }; -EMSG_T lt_expected = { "'<' expected", "'<' expected" }; -EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; -EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; -EMSG_T bad_dummy_arg = { "bad dummy arg", - "Bad dummy argument following DEFINE" }; -EMSG_T macro_too_long = { "macro too long", "Macro too long" }; -EMSG_T no_virtual_memory = { "out of memory", - "Insufficient memory for macro" }; -EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; -EMSG_T define_in_repeat = { "define in a repeat", "Define in a repeat" }; - -/*----------------------------------------------------------------------------*/ - -FILE *errorfile; -FILE *infile; -FILE *listfile; -FILE *listsave; -FILE *objectfile; -FILE *objectsave; - -char filename[NAMELEN]; -char listpathname[NAMELEN]; -char sympathname[NAMELEN]; -char objectpathname[NAMELEN]; -char *pathname; -char permpathname[NAMELEN]; - -WORD32 mac_count; /* Total macros defined. */ - -/* - * malloced macro bodies, indexed by sym->val dummies are evaluated at - * invocation time, and value saved in "args"; if recursive macros are - * desired (without conditionals, how would you escape?), keep a name - * list here and move symbols to "macinv" - */ -struct macdef { - int nargs; /* number of args */ - SYM_T args[MAC_MAX_ARGS+1]; /* symbol for each and one for "r" */ - char body[1]; /* malloc'ed accordingly */ -} *mac_defs[MAC_TABLE_LENGTH]; - -struct macinv { /* current macro invocation */ - char mac_line[LINELEN]; /* Saved macro invocation line. */ - WORD32 mac_cc; /* Saved cc after macro invocation. */ - char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ - struct macdef *defn; /* pointer to definition for dummies */ - struct macinv *prev; /* previous invocation in stack */ -} *curmacro; /* macro stack */ - -int nrepeats; /* count of nested repeats */ - -int list_lineno; -int list_pageno; -char list_title[LINELEN]; -BOOL list_title_set; /* Set if TITLE pseudo-op used. */ -char line[LINELEN]; /* Input line. */ -int lineno; /* Current line number. */ -int page_lineno; /* print line number on current page. */ -WORD32 listed; /* Listed flag. */ -WORD32 listedsave; - -WORD32 cc; /* Column Counter (char position in line). */ -WORD32 clc; /* Location counter */ -BOOL end_of_input; /* End of all input files. */ -int errors; /* Number of errors found so far. */ -BOOL error_in_line; /* TRUE if error on current line. */ -int errors_pass_1; /* Number of errors on pass 1. */ -int filix_curr; /* Index in argv to current input file. */ -int filix_start; /* Start of input files in argv. */ -int lexstartprev; /* Where previous lexeme started. */ -int lextermprev; /* Where previous lexeme ended. */ -int lexstart; /* Index of current lexeme on line. */ -int lexterm; /* Index of character after current lexeme. */ -int overbar; /* next saw an overbar in last token */ - -int nconst; /* number of "constants" blocks */ -int lit_count[MAX_CONSTANTS]; /* # of lits in each block in pass 1 */ -WORD32 lit_loc[MAX_CONSTANTS]; /* Base of literal blocks */ - -int noinput; /* don't punch loader */ - -int nvars; /* number of variables */ -WORD32 vars_addr; /* address of "variables" */ -WORD32 vars_end; /* end of "variables" */ - -/* pass 2 only; */ -int nlit; /* number of literals in litter[] */ -WORD32 litter[MAX_LITERALS]; /* literals */ - -WORD32 maxcc; /* Current line length. */ -BOOL nomac_exp; /* No macro expansion */ -WORD32 pass; /* Number of current pass. */ -BOOL print_permanent_symbols; -WORD32 radix; /* Default number radix. */ -BOOL rim_mode; /* RIM mode output. */ -BOOL sym_dump; /* punch symbol tape */ -int save_argc; /* Saved argc. */ -char **save_argv; /* Saved *argv[]. */ -WORD32 start_addr; /* Saved start address. */ -BOOL symtab_print; /* Print symbol table flag */ -BOOL xref; - -SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ - -/* initial data from SIMH v3.0 pdp1_stddev.c (different encoding of UC/LC) */ -#define UC 0100 /* Upper case */ -#define LC 0200 -#define CHARBITS 077 -#define BC LC|UC /* both case bits */ -#define BAD 014 /* unused concise code */ - -unsigned char ascii_to_fiodec[128] = { - BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, - BC|075, BC|036, BAD, BAD, BAD, BC|077, BAD, BAD, - BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, - BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, - BC|000, UC|005, UC|001, UC|004, BAD, BAD, UC|006, UC|002, - LC|057, LC|055, UC|073, UC|054, LC|033, LC|054, LC|073, LC|021, - LC|020, LC|001, LC|002, LC|003, LC|004, LC|005, LC|006, LC|007, - LC|010, LC|011, BAD, BAD, UC|007, UC|033, UC|010, UC|021, - LC|040, UC|061, UC|062, UC|063, UC|064, UC|065, UC|066, UC|067, - UC|070, UC|071, UC|041, UC|042, UC|043, UC|044, UC|045, UC|046, - UC|047, UC|050, UC|051, UC|022, UC|023, UC|024, UC|025, UC|026, - UC|027, UC|030, UC|031, UC|057, LC|056, UC|055, UC|011, UC|040, - UC|020, LC|061, LC|062, LC|063, LC|064, LC|065, LC|066, LC|067, - LC|070, LC|071, LC|041, LC|042, LC|043, LC|044, LC|045, LC|046, - LC|047, LC|050, LC|051, LC|022, LC|023, LC|024, LC|025, LC|026, - LC|027, LC|030, LC|031, BAD, UC|056, BAD, UC|003, BC|075 -}; - -/* for symbol punch tape conversion only!! */ -char fiodec_to_ascii[64] = { - 0, '1', '2', '3', '4', '5', '6', '7', - '8', '9', 0, 0, 0, 0, 0, 0, - '0', 0, 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z', 0, 0, 0, 0, 0, 0, - 0, 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 0, 0, 0, 0, 0, 0, - 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 0, 0, 0, 0, 0, 0 }; - -/* used at startup & for expunge */ -void -init_symtab(void) { - /* Place end marker in symbol table. */ - symtab[0] = sym_undefined; - symbol_top = 0; -} - -/* Function: main */ -/* Synopsis: Starting point. Controls order of assembly. */ -int -main( int argc, char *argv[] ) -{ - int ix; - int space; - - save_argc = argc; - save_argv = argv; - - /* Set the default values for global symbols. */ - print_permanent_symbols = FALSE; - nomac_exp = TRUE; - rim_mode = FALSE; /* default to loader tapes */ - sym_dump = FALSE; - noinput = FALSE; - - symtab_print = FALSE; - xref = FALSE; - pathname = NULL; - - /* init symbol table before processing arguments, so we can - * load symbol punch tapes on the fly - */ - - /* - * Setup the error file in case symbol table overflows while - * installing the permanent symbols. - */ - errorfile = stderr; - pass = 0; /* required for symbol table init */ - symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); - - if( symtab == NULL ) { - fprintf( stderr, "Could not allocate memory for symbol table.\n"); - exit( -1 ); - } - - init_symtab(); - - /* Enter the pseudo-ops into the symbol table */ - for( ix = 0; ix < DIM( pseudos ); ix++ ) - defineSymbol( pseudos[ix].name, pseudos[ix].val, pseudos[ix].type, 0 ); - - /* Enter the predefined symbols into the table. */ - /* Also make them part of the permanent symbol table. */ - for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) - defineSymbol( permanent_symbols[ix].name, - permanent_symbols[ix].val, - permanent_symbols[ix].type, 0 ); - - /* Get the options and pathnames */ - getArgs( argc, argv ); - - /* Do pass one of the assembly */ - pass = 1; - onePass(); - errors_pass_1 = errors; - - /* Set up for pass two */ - objectfile = fopen( objectpathname, "wb" ); - objectsave = objectfile; - - listfile = fopen( listpathname, "w" ); - listsave = listfile; - - /* XXX punch title into tape! */ - punchLeader( 0 ); - if (!rim_mode) { - punchLoader(); - punchLeader(5); - } - - if (nlit > 0) - constants(); /* implied "constants"? */ - - /* Do pass two of the assembly */ - errors = 0; - save_error_count = 0; - - if( xref ) { - /* Get the amount of space that will be required for the concordance */ - for( space = 0, ix = 0; ix < symbol_top; ix++ ) { - symtab[ix].xref_index = space; /* Index into concordance table. */ - space += symtab[ix].xref_count + 1; - symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ - } - /* Allocate & clear the necessary space. */ - xreftab = (WORD32 *) calloc( space, sizeof( WORD32 )); - } - pass = 2; - onePass(); - - objectfile = objectsave; - - /* Works great for trailer. */ - punchLeader( 1 ); - - /* undo effects of NOLIST for any following output to listing file. */ - listfile = listsave; - - /* Display value of error counter. */ - if( errors == 0 ) { - fprintf( listfile, "\n %s %s %s\n", s_no, s_errors, s_detected ); - } - else { - fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - fprintf( listfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - } - - if( symtab_print ) - printSymbolTable(); - - if( print_permanent_symbols ) - printPermanentSymbolTable(); - - if( xref ) - printCrossReference(); - - fclose( objectfile ); - fclose( listfile ); - if( errors == 0 && errors_pass_1 == 0 ) { - /* after closing objectfile -- we reuse the FILE *!! */ - if (sym_dump) - dump_symbols(); - } - else - remove( objectpathname ); - - return( errors != 0 ); -} /* main() */ - -/* read a word from a binary punch file */ -WORD32 -getw(FILE *f) -{ - int i, c; - WORD32 w; - - w = 0; - for (i = 0; i < 3;) { - c = getc(f); - if (c == -1) - return -1; - if (c & 0200) { /* ignore if ch8 not punched */ - w <<= 6; - w |= c & 077; - i++; - } - } - return w; -} - -/* - * "permute zone bits" like MACRO does for proper sorting - * (see routine "per" in MACRO) -- it's what DDT expects - * - * it's it's own inverse! - */ - -WORD32 -permute(WORD32 name) -{ - WORD32 temp; - - temp = name & 0202020; /* get zone bits */ - temp = ((temp << 1) & 0777777) | ((temp >> 17) & 1); /* rotate left */ - name ^= temp; /* flip zone bits */ - name ^= 0400000; /* toggle sign */ - return name; -} - -/* add a symbol from a "symbol punch" tape */ -void -addsym(WORD32 sym, WORD32 val) -{ - char name[4]; - - sym = permute(sym); - name[0] = fiodec_to_ascii[(sym >>12) & 077]; - name[1] = fiodec_to_ascii[(sym >> 6) & 077]; - name[2] = fiodec_to_ascii[sym & 077]; - name[3] = '\0'; - defineSymbol( name, val, LABEL, 0); -} - -void -read_symbols(char *fname) -{ - FILE *f; - - f = fopen(fname, "rb"); - if (!f) { - perror(fname); - exit(1); - } - - /* skip loader */ - for (;;) { - WORD32 w; - - w = getw(f); - if (w == -1) - goto err; /* XXX complain? */ - if ((w & OP_CODE) == JMP) - break; - if ((w & OP_CODE) != DIO) - goto err; /* XXX complain? */ - w = getw(f); - if (w == -1) - goto err; /* XXX complain? */ - } - - - /* XXX should push block reader down into a co-routine */ - for (;;) { - WORD32 start, end, sum; - - start = getw(f); - if ((start & OP_CODE) == JMP) { - fclose(f); - return; - } - - if (start == -1 || (start & OP_CODE) != DIO) - goto err; - - end = getw(f); - if (end == -1 || (end & OP_CODE) != DIO) - goto err; /* XXX complain? */ - - sum = start + end; - while (start < end) { - WORD32 sym, val; - sym = getw(f); - if (sym == -1) - goto err; - sum += sym; - start++; - /* XXX handle block boundaries? */ - if (start >= end) - goto err; - val = getw(f); - if (val == -1) - goto err; - /*printf("%06o %06o\n", sym, val);*/ - addsym(sym, val); - sum += val; - start++; - } - start = getw(f); /* eat checksum XXX verify? */ - if (start == -1) - goto err; - /* roll over all the overflows at once */ - if (sum & ~0777777) { - sum = (sum & 0777777) + (sum >> 18); - if (sum & 01000000) /* one more time */ - sum++; - } - if (start != sum) - goto err; - } -err: - fprintf(stderr, "error reading symbol file %s\n", fname); - exit(1); -} - -/* Function: getArgs */ -/* Synopsis: Parse command line, set flags accordingly and setup input and */ -/* output files. */ -void getArgs( int argc, char *argv[] ) -{ - WORD32 len; - WORD32 ix, jx; - - /* Set the defaults */ - infile = NULL; - listfile = NULL; - listsave = NULL; - objectfile = NULL; - objectsave = NULL; - - for( ix = 1; ix < argc; ) - { - if( argv[ix][0] == '-' ) - { - char *switches = argv[ix++]; - for( jx = 1; switches[jx] != 0; jx++ ) - { - switch( switches[jx] ) - { - case 'd': - symtab_print = TRUE; - break; - - case 'r': - rim_mode = TRUE; /* punch pure rim-mode tapes */ - break; - - case 's': - sym_dump = TRUE; - break; - - case 'm': - nomac_exp = FALSE; - break; - - case 'p': - print_permanent_symbols = TRUE; - break; - - case 'x': - xref = TRUE; - break; - - case 'S': - if (ix <= argc) - read_symbols(argv[ix++]); - break; - - default: - fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); - fprintf( stderr, " -d -- dump symbol table\n" ); - fprintf( stderr, " -m -- output macro expansions\n" ); - fprintf( stderr, " -p -- output permanent symbols to file\n" ); - fprintf( stderr, " -r -- output RIM format file\n" ); - fprintf( stderr, " -s -- output symbol punch tape to file\n" ); - fprintf( stderr, " -S file -- read symbol punch tape\n" ); - fprintf( stderr, " -x -- output cross reference to file\n" ); - fflush( stderr ); - exit( -1 ); - } /* end switch */ - } /* end for */ - } - else - { - filix_start = ix; - pathname = argv[ix]; - break; - } - } /* end for */ - - if( pathname == NULL ) - { - fprintf( stderr, "%s: no input file specified\n", argv[0] ); - exit( -1 ); - } - - len = strlen( pathname ); - if( len > NAMELEN - 5 ) - { - fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); - exit( -1 ); - } - - /* Now make the pathnames */ - /* Find last '.', if it exists. */ - jx = len - 1; - while( pathname[jx] != '.' && pathname[jx] != '/' - && pathname[jx] != '\\' && jx >= 0 ) - { - jx--; - } - - switch( pathname[jx] ) - { - case '.': - break; - - case '/': - case '\\': - jx = len; - break; - - default: - break; - } - - /* Add the pathname extensions. */ - strncpy( objectpathname, pathname, jx ); - objectpathname[jx] = '\0'; - strcat( objectpathname, ".rim"); - - strncpy( listpathname, pathname, jx ); - listpathname[jx] = '\0'; - strcat( listpathname, ".lst" ); - - strncpy( permpathname, pathname, jx ); - permpathname[jx] = '\0'; - strcat( permpathname, ".prm" ); - - strncpy( sympathname, pathname, jx ); - sympathname[jx] = '\0'; - strcat( sympathname, ".sym" ); - - /* Extract the filename from the path. */ - if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) - pathname[1] = '\\'; /* MS-DOS style pathname */ - - jx = len - 1; - while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) - jx--; - strcpy( filename, &pathname[jx + 1] ); -} /* getArgs() */ - - -int -invokeMacro(int index) -{ - struct macinv *mip; - struct macdef *mdp; - int jx; - - mdp = mac_defs[index]; - if (mdp == NULL || mdp->body[0] == '\0') - return 0; - - /* Find arguments. */ - while (ISBLANK(line[lexstart])) - next(0); - - mip = calloc(1, sizeof(struct macinv)); - if (!mip) { - fprintf(stderr, "could not allocate memory for macro invocation\n"); - exit(1); - } - mip->defn = mdp; - - /* evaluate args, saving values in SYM_T entries in defn. - * (cannot have recursive macros) - */ - mdp->args[0].val = clc; /* r is location at start */ - for( jx = 1; !ISDONE(line[lexstart]) && jx <= MAC_MAX_ARGS; ) { - WORD32 val; - - next(0); - if (ISDONE(line[lexstart])) - break; - - if (line[lexstart] == ',') - next(0); - - while( ISBLANK( line[lexstart] )) - next(0); - - if (ISDONE(line[lexstart])) - break; - - val = getExprs(); - - /* ignore excess values silently? */ - if (jx <= mdp->nargs) - mdp->args[jx].val = val; - jx++; - } /* end for */ - - /* XXX complain if too few actuals? -- nah */ - while (jx <= mdp->nargs) - mdp->args[jx++].val = 0; - - strcpy(mip->mac_line, line); /* save line */ - mip->mac_cc = cc; /* save position in line */ - mip->mac_ptr = mdp->body; - mip->prev = curmacro; /* push the old entry */ - curmacro = mip; /* step up to the plate! */ - return 1; -} - -/* process input; used by onePass and repeat */ -void -processLine() { - if (!list_title_set) { - char *cp; - - /* assert(sizeof(title) >= sizeof(line)); */ - strcpy(list_title, line); - - if ((cp = strchr(list_title, '\n'))) - *cp = '\0'; - - if (list_title[0]) { - list_title_set = TRUE; - fprintf(stderr, "%s - pass %d\n", list_title, pass ); - /* XXX punch title into tape banner (until an '@' seen) */ - } - return; - } - - for (;;) { - int jx; - SYM_T evalue; - - next(0); - if( end_of_input ) - return; - - if( ISEND( line[lexstart] )) { - if (line[lexstart] != '\t') - return; - continue; - } - if (line[lexstart] == '/') /* comment? */ - return; /* done */ - - /* look ahead for 'exp/' */ - /* skip until whitespace or terminator */ - for( jx = lexstart; jx < maxcc; jx++ ) - if( ISBLANK(line[jx]) || ISDONE(line[jx])) - break; - if( line[jx] == '/') { /* EXP/ set location */ - WORD32 newclc; - - newclc = getExprs(); - - /* Do not change Current Location Counter if an error occurred. */ - if( !error_in_line ) - clc = newclc; - - printLine( line, newclc, 0, LINE_LOC ); - cc = jx + 1; - next(0); /* discard slash */ - continue; - } - - switch( line[lexterm] ) { - case ',': - if( isLexSymbol()) { - WORD32 val; - SYM_T *sym; - char name[SYMLEN]; - - /* Use lookup so symbol will not be counted as reference. */ - sym = lookup(lexemeToName(name, lexstart, lexterm), UNDEFINED); - - if (curmacro) { - /* relative during macro expansion!! */ - val = clc - curmacro->defn->args[0].val; - } - else - val = clc; - - if( M_DEFINED( sym->type )) { - if( sym->val != val && pass == 2 ) - errorSymbol( &duplicate_label, sym->name, lexstart ); - sym->type |= DUPLICATE; /* XXX never used! */ - } - /* Must call define on pass 2 to generate concordance. */ - defineLexeme( lexstart, lexterm, val, LABEL ); - } - else if (isdigit(line[lexstart])) { /* constant, */ - int i; - WORD32 val = 0; - - for( i = lexstart; i < lexterm; i++ ) { - if( isdigit( line[i] )) { - int digit; - digit = line[i] - '0'; - if( digit >= radix ) { - errorLexeme( &number_not_radix, i ); - val = 0; - break; - } - val = val * radix + digit; - } - else { - errorLexeme( ¬_a_number, lexstart ); - val = 0; - break; - } - } - if (i == lexterm) { - if( clc != val && pass == 2 ) - errorLexeme( &duplicate_label, lexstart); /* XXX */ - } - } - else - errorLexeme( &label_syntax, lexstart ); - next(0); /* skip comma */ - continue; - - case '=': - if( isLexSymbol()) { - WORD32 start, term, val; - - start = lexstart; - term = lexterm; - next(0); /* skip symbol */ - next(0); /* skip trailing = */ - val = getExprs(); - defineLexeme( start, term, val, DEFINED ); - printLine( line, 0, val, LINE_VAL ); - } - else { - errorLexeme( &symbol_syntax, lexstartprev ); - next(0); /* skip symbol */ - next(0); /* skip trailing = */ - getExprs(); /* skip expression */ - } - continue; - } /* switch on terminator */ - - if( isLexSymbol()) { - SYM_T *sym; - WORD32 val; - - sym = evalSymbol(); - val = sym->val; - if( M_MACRO(sym->type)) { - if (!invokeMacro(val)) - next(0); /* bad defn? or body is empty! */ - continue; - } /* macro invocation */ - else if( M_PSEUDO(sym->type)) { /* NO EPSEUDOs */ - pseudo( (PSEUDO_T)val & 0777777 ); - continue; - } /* pseudo */ - } /* macro, or non-char pseudo */ - - evalue = getExpr(); - if (evalue.type != PSEUDO) { /* not a bare pseudo-op? */ - if (line[lexstart] == ',') { /* EXP, */ - if(evalue.val != clc && pass == 2 ) - errorLexeme( &duplicate_label, lexstart); /* XXX */ - } - else if (line[lexstart] == '/') { /* EXP/ */ - clc = evalue.val; - printLine( line, clc, 0, LINE_LOC ); - next(0); - } - else { - punchOutObject( clc, evalue.val & 0777777); /* punch it! */ - incrementClc(); - } - } - } /* forever */ -} - -/* Function: onePass */ -/* Synopsis: Do one assembly pass. */ -void onePass() { - int ix; - - clc = 4; /* Default location is 4 */ - start_addr = 0; /* No starting address. */ - nconst = 0; /* No constant blocks seen */ - nvars = 0; /* No variables seen */ - - while (curmacro) { /* pop macro stack */ - struct macinv *mp; - - mp = curmacro->prev; - free(curmacro); - curmacro = mp; - } - - for( ix = 0; ix < mac_count; ix++) { - if (mac_defs[ix]) - free( mac_defs[ix] ); - mac_defs[ix] = NULL; - } - mac_count = 0; /* No macros defined. */ - - listed = TRUE; - lineno = 0; - list_pageno = 0; - list_lineno = 0; - list_title_set = FALSE; - page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ - radix = 8; /* Initial radix is octal (base 8). */ - - /* Now open the first input file. */ - end_of_input = FALSE; - filix_curr = filix_start; /* Initialize pointer to input files. */ - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], - save_argv[filix_curr] ); - exit( -1 ); - } - - for (;;) { - readLine(); - if (end_of_input) { - eob(); - fclose( infile ); - return; - } - processLine(); - } /* forever */ -} /* onePass */ - - -/* Function: getExprs */ -/* Synopsys: gutted like a fish */ -WORD32 getExprs() -{ - SYM_T sym; - - sym = getExpr(); - if (sym.type == PSEUDO) - errorMessage( &value_required, lexstart ); /* XXX wrong pointer? */ - - return sym.val & 0777777; -} /* getExprs */ - - -SYM_T getExpr() -{ - SYM_T sym; - - sym = eval(); - - /* Here we assume the current lexeme is the operator separating the */ - /* previous operator from the next, if any. */ - - for (;;) { - int space; - /* - * falling out of switch breaks loop and returns from routine - * so if you want to keep going, you must "continue"!! - */ - space = FALSE; - switch( line[lexstart] ) { - case ' ': - space = TRUE; - /* fall */ - case '+': /* add */ - next(1); /* skip operator */ - if (space && ISEND(line[lexstart])) /* tollerate a trailing space */ - return sym; - sym.val += eval().val; /* XXX look at type? */ - sym.type = DEFINED; - if( sym.val >= 01000000 ) - sym.val = ( sym.val + 1 ) & 0777777; - continue; - - case '-': /* subtract */ - next(1); /* skip over the operator */ - sym.val += eval().val ^ 0777777; /* XXX look at type? */ - sym.type = DEFINED; - if( sym.val >= 01000000 ) - sym.val = ( sym.val + 1 ) & 0777777; - continue; - - case '*': /* multiply */ - next(1); /* skip over the operator */ - sym.val *= eval().val; - sym.type = DEFINED; - if( sym.val >= 01000000 ) - sym.val = ( sym.val + 1 ) & 0777777; - continue; - -#if 0 - case '%': /* divide !??? */ - /* - * neither '%' nor the divide symbol appear in FIO-DEC, - * does any known program use such an operator? - * Easily confused for "MOD", which is how C uses '%'! - */ - next(1); - sym.val /= eval().val; - sym.type = DEFINED; - continue; -#endif - - case '&': /* and */ - next(1); /* skip over the operator */ - sym.val &= eval().val; - sym.type = DEFINED; - continue; - - case '!': /* or */ - next(1); /* skip over the operator */ - sym.val |= eval().val; - sym.type = DEFINED; - continue; - - case '/': - case ')': - case ']': - case ':': - case ',': - break; - - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - sym.val = 0; - break; - - default: - if (!ISEND(line[lexstart])) { - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - sym.val = 0; - break; - } - } /* switch */ - break; /* break loop!! */ - } /* "forever" */ - return( sym ); -} /* getExpr */ - -/* - * return fio-dec code for next char - * embeds shifts as needed - */ -int -nextfiodec(int *ccase, int delim) -{ - unsigned char c; - - for (;;) { - if (cc >= maxcc) { - if (delim == -1) - return -1; - - /* XXX MUST NOT BE IN A REPEAT!! */ - readLine(); /* danger will robinson! */ - if (end_of_input) - return -1; - } - c = line[cc]; - switch (c) { - case '\n': - c = '\r'; - break; - case '\r': - continue; - } - break; - } - - if (delim != -1 && c == delim) { - if (*ccase == LC) { - cc++; /* eat delim */ - return -1; - } - *ccase = LC; - return CONCISE_LC; /* shift down first */ - } - - if (c > 0177) { /* non-ascii */ - errorMessage( &illegal_character, cc ); - c = 0; /* space?! */ - } - - c = ascii_to_fiodec[c&0177]; - if (c == BAD) { - errorMessage( &illegal_character, cc ); - c = 0; /* space?! */ - } - - if (!(c & *ccase)) { /* char not in current case? */ - *ccase ^= BC; /* switch case */ - if (*ccase == LC) - return CONCISE_LC; /* shift down */ - else - return CONCISE_UC; /* shift up */ - } - cc++; - return c & CHARBITS; -} - -/* - * Function: flex - * Synopsis: Handle data for "flexo" pseudo - * handle upper case by doing shifts - */ - -WORD32 flex() -{ - WORD32 w; - int shift; - int ccase; - - if (line[lexstart] == ' ') /* always? */ - next(0); - - /* original version appears to take next 3 characters, - * REGARDLESS of what they are (tab, newline, space?)! - */ - w = 0; - ccase = LC; /* current case */ - for (shift = 12; shift >= 0; shift -= 6) { - unsigned char c; - if( lexstart >= maxcc ) - break; - - c = line[lexstart]; - if (c == '\t' || c == '\n') { - if (ccase == LC) - break; - c = CONCISE_LC; /* shift down first */ - } - else { - if (c > 0177) { /* non-ascii */ - errorMessage( &illegal_character, lexstart ); - c = 0; - } - - c = ascii_to_fiodec[c&0177]; - if (c == BAD) { - errorMessage( &illegal_character, lexstart ); - c = 0; - } - - if (!(c & ccase)) { /* char not in current case? */ - ccase ^= BC; /* switch case */ - if (ccase == LC) - c = CONCISE_LC; /* shift down */ - else - c = CONCISE_UC; /* shift up */ - } - else - lexstart++; - } - w |= (c & CHARBITS) << shift; - } - /* error to get here w/ case == UC? nah. shift down could be next */ - return w; -} /* flex */ - -/* - * Function: getChar - * Synopsis: Handle data for "char" pseudo - */ - -WORD32 getChar() -{ - unsigned char c, pos; - - if( cc >= maxcc ) - return 0; /* XXX error? */ - pos = line[cc++]; - if (pos != 'l' && pos != 'm' && pos != 'r') { - errorMessage( &illegal_character, lexstart ); - return 0; - } - - if( cc >= maxcc ) - return 0; /* XXX error? */ - - c = line[cc++]; - if (c > 0177) { - errorMessage( &illegal_character, lexstart ); - c = 0; - } - - c = ascii_to_fiodec[c]; - if (c == BAD) { - errorMessage( &illegal_character, lexstart ); - c = 0; - } - - if (!(c & LC)) { /* upper case only char? */ - c = CONCISE_UC; /* take a shift up */ - cc--; /* and leave char for next luser */ - } - - c &= CHARBITS; - switch (pos) { - case 'l': return c << 12; - case 'm': return c << 6; - case 'r': return c; - } - /* should not happen */ - return 0; -} /* flex */ - -/* Function: eval */ -/* Synopsis: Get the value of the current lexeme, and advance.*/ -SYM_T eval2() -{ - WORD32 digit; - WORD32 from; - SYM_T *sym; - WORD32 val; - SYM_T sym_eval; - - sym_eval.type = DEFINED; - sym_eval.name[0] = '\0'; - sym_eval.val = sym_eval.xref_index = sym_eval.xref_count = 0; - - val = 0; - - if( isLexSymbol()) { - sym = evalSymbol(); - if(!M_DEFINED( sym->type )) { - if( pass == 2 ) - errorSymbol( &undefined_symbol, sym->name, lexstart ); - next(1); - return( *sym ); - } - else if( M_PSEUDO(sym->type) || M_EPSEUDO(sym->type)) { - switch (sym->val) { - case DECIMAL: - radix = 10; - sym_eval.type = PSEUDO; - sym_eval.val = 0; /* has zero as a value! */ - break; - case OCTAL: - radix = 8; - sym_eval.type = PSEUDO; - sym_eval.val = 0; /* has zero as a value */ - break; - case FLEX: - next(1); /* skip keyword */ - sym_eval.val = flex(); - break; - case CHAR: - next(1); /* skip keyword */ - sym_eval.val = getChar(); - break; - default: - errorSymbol( &value_required, sym->name, lexstart ); - sym_eval.type = sym->type; - sym_eval.val = 0; - break; - } - next(1); - return( sym_eval ); - } - else if( M_MACRO( sym->type )) - { - if( pass == 2 ) - { - errorSymbol( &misplaced_symbol, sym->name, lexstart ); - } - sym_eval.type = sym->type; - sym_eval.val = 0; - next(1); - return( sym_eval ); - } - else - { - next(1); - return( *sym ); - } - } /* symbol */ - else if( isdigit( line[lexstart] )) { - from = lexstart; - val = 0; - while( from < lexterm ) { - if( isdigit( line[from] )) { - digit = line[from++] - '0'; - if( digit >= radix ) { - errorLexeme( &number_not_radix, from - 1 ); - val = 0; - break; - } - val = val * radix + digit; - } - else { - errorLexeme( ¬_a_number, lexstart ); - val = 0; - break; - } - } - next(1); - sym_eval.val = val; - return( sym_eval ); - } /* digit */ - else { - switch( line[lexstart] ) { - case '.': /* Value of Current Location Counter */ - val = clc; - next(1); - break; - case '(': /* Generate literal */ - next(1); /* Skip paren */ - val = getExprs(); /* recurse */ - if( line[lexstart] == ')' ) - next(1); /* Skip end paren */ - sym_eval.val = literal(val); - return sym_eval; - case '[': /* parens!! */ - next(1); - sym_eval.val = getExprs(); /* mutual recursion */ - if( line[lexstart] == ']' ) - next(1); /* Skip close bracket */ - else - errorMessage( &illegal_character, lexstart ); - return sym_eval; - default: - switch( line[lexstart] ) { - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - break; - default: - errorMessage( &illegal_character, lexstart ); - break; - } /* error switch */ - val = 0; /* On error, set value to zero. */ - next(1); /* Go past illegal character. */ - } /* switch on first char */ - } /* not symbol or number */ - sym_eval.val = val; - return( sym_eval ); -} /* eval2 */ - - -SYM_T eval() { - SYM_T sym; - - switch (line[lexstart]) { - case '-': /* unary - */ - next(1); - sym = eval2(); /* skip op */ - sym.val ^= 0777777; - break; - case '+': /* unary + */ - next(1); /* skip op */ - /* fall */ - default: - sym = eval2(); - } - return sym; -} - -/* Function: incrementClc */ -/* Synopsis: Set the next assembly location. Test for collision with */ -/* the literal tables. */ -WORD32 incrementClc() -{ - clc = (( clc + 1 ) & ADDRESS_FIELD ); - return( clc ); -} /* incrementClc */ - - -/* Function: readLine */ -/* Synopsis: Get next line of input. Print previous line if needed. */ -void readLine() -{ - BOOL ffseen; - WORD32 ix; - WORD32 iy; - char inpline[LINELEN]; - - /* XXX panic if nrepeats > 0 (if self-feeding, do the backup here?) */ - - listLine(); /* List previous line if needed. */ - error_in_line = FALSE; /* No error in line. */ - - if(curmacro && *curmacro->mac_ptr == '\0') { /* end of macro? */ - struct macinv *mp; - - listed = TRUE; /* Already listed. */ - - /* Restore invoking line. */ - strcpy(line, curmacro->mac_line); - cc = lexstartprev = curmacro->mac_cc; /* Restore cc. */ - maxcc = strlen( line ); /* Restore maxcc. */ - - mp = curmacro->prev; /* pop stack */ - free(curmacro); - curmacro = mp; - - return; - } /* end of macro */ - - cc = 0; /* Initialize column counter. */ - lexstartprev = 0; - if( curmacro ) { /* Inside macro? */ - char mc; - - maxcc = 0; - do { - - mc = *curmacro->mac_ptr++; /* Next character. */ - /* watch for overflow? how could it?? */ - line[maxcc++] = mc; - } while( !ISEND( mc )); /* note: terminates on tab?! */ - line[maxcc] = '\0'; - listed = nomac_exp; - return; - } /* inside macro */ - - lineno++; /* Count lines read. */ - listed = FALSE; /* Mark as not listed. */ - READ_LINE: - if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { - filix_curr++; /* Advance to next file. */ - if( filix_curr < save_argc ) { /* More files? */ - fclose( infile ); - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], - save_argv[filix_curr] ); - exit( -1 ); - } - list_title_set = FALSE; - goto READ_LINE; - } - else - end_of_input = TRUE; - } /* fgets failed */ - - ffseen = FALSE; - for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { - if( inpline[ix] == '\f' ) { - if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); - ffseen = TRUE; - } - else - line[iy++] = inpline[ix]; - } - line[iy] = '\0'; - - /* If the line is terminated by CR-LF, remove, the CR. */ - if( line[iy - 2] == '\r' ) { - iy--; - line[iy - 1] = line[iy - 0]; - line[iy] = '\0'; - } - maxcc = iy; /* Save the current line length. */ -} /* readLine */ - - -/* Function: listLine */ -/* Synopsis: Output a line to the listing file. */ -void listLine() -/* generate a line of listing if not already done! */ -{ - if( listfile != NULL && listed == FALSE ) - { - printLine( line, 0, 0, LINE ); - } -} /* listLine */ - - -/* Function: printPageBreak */ -/* Synopsis: Output a Top of Form and listing header if new page necessary. */ -void printPageBreak() -{ - if( page_lineno >= LIST_LINES_PER_PAGE ) - /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ - { - topOfForm( list_title, NULL ); - } -} /* printPageBreak */ - - -/* Function: printLine */ -/* Synopsis: Output a line to the listing file with new page if necessary. */ -void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) -{ - if( listfile == NULL ) - { - save_error_count = 0; - return; - } - - printPageBreak(); - - list_lineno++; - page_lineno++; - switch( linestyle ) - { - default: - case LINE: - fprintf( listfile, "%5d ", lineno ); - fputs( line, listfile ); - listed = TRUE; - break; - - case LINE_VAL: - if( !listed ) - { - fprintf( listfile, "%5d %6.6o ", lineno, val ); - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %6.6o\n", val ); - } - break; - - case LINE_LOC: - if( !listed ) - { - fprintf( listfile, "%5d %5.5o ", lineno, loc ); - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %5.5o\n", loc ); - } - break; - - case LINE_LOC_VAL: - if( !listed ) - { - fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %5.5o %6.6o\n", loc, val ); - } - break; - - case LOC_VAL: - fprintf( listfile, " %5.5o %6.6o\n", loc, val ); - break; - } - printErrorMessages(); -} /* printLine */ - - -/* Function: printErrorMessages */ -/* Synopsis: Output any error messages from the current list of errors. */ -void printErrorMessages() -{ - WORD32 ix; - WORD32 iy; - - if( listfile != NULL ) - { - /* If any errors, display them now. */ - for( iy = 0; iy < save_error_count; iy++ ) - { - printPageBreak(); - fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); - if( error_list[iy].col >= 0 ) - { - for( ix = 0; ix < error_list[iy].col; ix++ ) - { - if( line[ix] == '\t' ) - { - putc( '\t', listfile ); - } - else - { - putc( ' ', listfile ); - } - } - fputs( "^", listfile ); - list_lineno++; - page_lineno++; - } - fputs( "\n", listfile ); - } - } - save_error_count = 0; -} /* printErrorMessages */ - - -/* Function: punchObject */ -/* Synopsis: Put one character to object file */ -void punchObject( WORD32 val ) -{ - val &= 0377; - if( objectfile != NULL ) - fputc( val, objectfile ); -} /* punchObject */ - -/* Function: punchTriplet */ -/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ -void punchTriplet( WORD32 val ) -{ - punchObject((( val >> 12) & 077) | 0200 ); - punchObject((( val >> 6 ) & 077) | 0200 ); - punchObject(( val & 077) | 0200 ); -} /* punchTriplet */ - -void -eob() { - /* in case no "start" in file (an error?) */ -} - -/* Function: punchLeader */ -/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ -/* documentation. Paper tape has 10 punches per inch. */ -void punchLeader( WORD32 count ) -{ - WORD32 ix; - - /* If value is zero, set to the default of 2 feet of leader. */ - count = ( count == 0 ) ? 240 : count; - - if( objectfile != NULL ) - { - for( ix = 0; ix < count; ix++ ) - { - fputc( 0, objectfile ); - } - } -} /* punchLeader */ - -/* Function: punchOutObject */ -/* Synopsis: Output the current line and then then punch value to the */ -/* object file. */ -void punchOutObject( WORD32 loc, WORD32 val ) -{ - printLine( line, loc, val, LINE_LOC_VAL ); - punchLocObject( loc, val ); -} /* punchOutObject */ - - -/* Function: punchLocObjectRIM */ -/* Synopsis: Output the word in RIM mode */ -void punchLocObjectRIM( WORD32 loc, WORD32 val ) -{ - punchTriplet( DIO | loc ); - punchTriplet( val ); -} /* punchLocObject */ - -/* punch loader in RIM mode */ -void -punchLoader() { - int i; - - if (noinput) - return; - - for (i = 0; i < DIM(loader); i++) - punchLocObjectRIM(LOADERBASE+i, loader[i]); - punchTriplet( JMP | LOADERBASE ); -} - -/* - * flush out loader buffer; output a block: - * DIO start - * DIO end+1 - * .... data .... - * sum - */ -#define PW(X) { WORD32 x = X; sum += x; punchTriplet(x); } -void -flushLoader() { - WORD32 sum; - int i; - - if (loaderbufcount == 0) - return; - - sum = 0; - PW( DIO | loaderbufstart ); - PW( DIO | loaderbufstart + loaderbufcount ); - for (i = 0; i < loaderbufcount; i++) - PW( loaderbuf[i] ); - - /* roll over all the overflows at once */ - if (sum & ~0777777) - sum = (sum & 0777777) + (sum >> 18); - if (sum & 01000000) /* one more time */ - sum++; - PW( sum ); - - punchLeader(5); - loaderbufcount = 0; -} - -void punchLocObject( WORD32 loc, WORD32 val ) -{ - if (!rim_mode) { - if ((loc & LOADERBUFMASK) == 0 || /* full/force alignment */ - loaderbufcount > 0 && - loc != loaderbufstart + loaderbufcount) /* disjoint */ - flushLoader(); - if (loaderbufcount == 0) - loaderbufstart = loc; - loaderbuf[loaderbufcount++] = val; - } - else - punchLocObjectRIM( loc, val ); -} - -/* Function: literal */ -/* Synopsis: Add a value to the literal pool */ -WORD32 -literal( WORD32 value ) -{ - int i; - - if (nconst >= MAX_CONSTANTS) { - fprintf(stderr, "too many 'constants'; increase MAX_CONSTANTS\n"); - exit(1); - } - - if (pass == 1) { - if (++lit_count[nconst] == MAX_LITERALS) { - fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); - exit(1); - } - return lit_count[nconst]; - } - -#if 1 - /* - * pool constants; makes for a shorter tape - * (but "middle" constants blocks can't shrink) - */ - for (i = 0; i < nlit; i++) - if (litter[i] == value) - return lit_loc[nconst] + i; -#endif - - /* paranoia */ - if (nlit == MAX_LITERALS) { - fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); - exit(1); - } - - /* not found, save it */ - litter[nlit] = value; - - /* use base for this block, determined on pass1 */ - return lit_loc[nconst] + nlit++; -} /* literal */ - - -/* Function: printSymbolTable */ -/* Synopsis: Output the symbol table. */ -/* XXX now prints FIXED symbols too */ -void printSymbolTable() -{ - int ix; - int symbol_lines; - SYM_T *sym; - char mark; - - symbol_lines = 0; - for (ix = 0, sym = symtab; ix < symbol_top; ix++, sym++) { - if (M_FIXED(sym->type) || M_PSEUDO(sym->type) || - M_MACRO(sym->type) || M_EPSEUDO(sym->type)) - continue; - - if (symbol_lines == 0) { - topOfForm( list_title, s_symtable ); - symbol_lines = LIST_LINES_PER_PAGE; - } - - switch( sym->type & ( DEFINED | REDEFINED )) { - case UNDEFINED: - mark = '?'; - break; - - case REDEFINED: - mark = '#'; - break; - - default: - mark = ' '; - break; - } - fprintf( listfile, "%c%-6.6s %6.6o\n", mark, sym->name, sym->val ); - symbol_lines--; - } -} /* printSymbolTable */ - - -/* Function: printPermanentSymbolTable */ -/* Synopsis: Output the permanent symbol table to a file suitable for */ -/* being input after the EXPUNGE pseudo-op. */ -void printPermanentSymbolTable() -{ - int ix; - FILE *permfile; - - if(( permfile = fopen( permpathname, "w" )) == NULL ) - { - exit( 2 ); - } - - fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); - fprintf( permfile, " expunge\n/\n" ); - - for( ix = 0; ix < symbol_top; ix++ ) - { - int type = symtab[ix].type; - if( M_FIXED(type) && !M_PSEUDO(type) && !M_EPSEUDO(type) ) - fprintf( permfile, "\t%s=%o\n", - symtab[ix].name, symtab[ix].val ); - } - fclose( permfile ); -} /* printPermanentSymbolTable */ - - -/* Function: printCrossReference */ -/* Synopsis: Output a cross reference (concordance) for the file being */ -/* assembled. */ -void printCrossReference() -{ - int ix; - int xc; - int xc_index; - int xc_refcount; - int xc_cols; - SYM_T *sym; - - /* Force top of form for first page. */ - page_lineno = LIST_LINES_PER_PAGE; - - list_lineno = 0; - - for( ix = 0, sym = symtab; ix < symbol_top; ix++, sym++ ) { - if (M_FIXED(sym->type) && xreftab[sym->xref_index] == 0) - continue; - list_lineno++; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - topOfForm( list_title, s_xref ); - - fprintf( listfile, "%5d", list_lineno ); - - /* Get reference count & index into concordance table for this symbol */ - xc_refcount = sym->xref_count; - xc_index = sym->xref_index; - /* Determine how to label symbol on concordance. */ - /* XXX flag variables? */ - switch( sym->type & ( DEFINED | REDEFINED )) { - case UNDEFINED: - fprintf( listfile, " U "); - break; - - case REDEFINED: - fprintf( listfile, " M %5d ", xreftab[xc_index] ); - break; - - default: - fprintf( listfile, " A %5d ", xreftab[xc_index] ); - break; - } - fprintf( listfile, "%-6.6s ", sym->name ); - - /* Output the references, 8 numbers per line after symbol name. */ - for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { - if( xc_cols >= XREF_COLUMNS ) { - xc_cols = 0; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - topOfForm( list_title, s_xref); - list_lineno++; - fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); - } - fprintf( listfile, " %5d", xreftab[xc_index + xc] ); - } - fprintf( listfile, "\n" ); - } /* for */ -} /* printCrossReference */ - - -/* Function: topOfForm */ -/* Synopsis: Prints title and sub-title on top of next page of listing. */ -void topOfForm( char *title, char *sub_title ) -{ - char temp[10]; - - list_pageno++; - strcpy( temp, s_page ); - sprintf( temp, "%s %d", s_page, list_pageno ); - - if (!listfile) - return; - - /* Output a top of form if not the first page of the listing. */ - if( list_pageno > 1 ) - fprintf( listfile, "\f" ); - - fprintf( listfile, "\n %-63s %10s\n", title, temp ); - - /* Reset the current page line counter. */ - page_lineno = 1; - if( sub_title != NULL ) - { - fprintf( listfile, "%80s\n", sub_title ); - page_lineno++; - } - else - { - fprintf( listfile, "\n" ); - page_lineno++; - } - fprintf( listfile, "\n" ); - page_lineno++; -} /* topOfForm */ - - -/* Function: lexemeToName */ -/* Synopsis: Convert the current lexeme into a string. */ -char *lexemeToName( char *name, WORD32 from, WORD32 term ) -{ - int to; - - to = 0; - while( from < term && to < SYMLEN-1) { - char c = line[from++]; - if (ISOVERBAR(c)) - continue; - name[to++] = c; - } - name[to] = '\0'; - - return( name ); -} /* lexemeToName */ - -/* Function: defineLexeme */ -/* Synopsis: Put lexeme into symbol table with a value. */ -SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ - WORD32 term, /* end+1 of lexeme being defined. */ - WORD32 val, /* value of lexeme being defined. */ - SYMTYP type ) /* how symbol is being defined. */ -{ - char name[SYMLEN]; - - lexemeToName( name, start, term); - return( defineSymbol( name, val, type, start )); -} /* defineLexeme */ - - -/* Function: defineSymbol */ -/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ -/* not already in table. */ -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) -{ - SYM_T *sym; - WORD32 xref_count; - - if( strlen( name ) < 1 ) - { - return( &sym_undefined ); /* Protect against non-existent names. */ - } - sym = lookup( name, type ); - xref_count = 0; /* Set concordance for normal defintion. */ - - if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) - { - if( pass == 2 ) - { - errorSymbol( &redefined_symbol, sym->name, start ); - type = type | REDEFINED; - sym->xref_count++; /* Referenced symbol, count it. */ - xref_count = sym->xref_count; - /* moved inside "if pass2" -plb 10/2/03 allow redefinition - * of predefined symbols during pass1 - */ - return ( sym ); - } - } - - if( pass == 2 && xref ) - { - /* Put the definition line number in the concordance table. */ - /* Defined symbols are not counted as references. */ - if (sym->xref_index >= 0) { /* beware macro dummies */ - xreftab[sym->xref_index] = lineno; - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + xref_count] = lineno; - } - } - - /* Now set the value and the type. */ - sym->val = val & 0777777; - sym->type = type; - return( sym ); -} /* defineSymbol */ - - -/* Function: lookup */ -/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ -/* table as undefined. Return address of symbol in table. */ -SYM_T *lookup( char *name, int type ) -{ - int ix; /* Insertion index */ - int lx; /* Left index */ - int rx; /* Right index */ - SYM_T *best; /* best match */ - SYM_T *sym; - - /* YIKES! Search dummies (and "R") before anything else!! */ - if (curmacro && curmacro->defn) { - struct macdef *mdp = curmacro->defn; - int i; - - for (i = 0, sym = mdp->args; i <= mdp->nargs; i++, sym++) - if (strcmp(name, sym->name) == 0) - return sym; - } - - lx = 0; - rx = symbol_top - 1; - best = NULL; - while (lx <= rx) { - int mx = (lx + rx) / 2; /* Find center of search area. */ - int compare; - - sym = symtab + mx; - - compare = strcmp(name, sym->name); - if (compare < 0) - rx = mx - 1; - else if (compare > 0) - lx = mx + 1; - else { /* match */ - if (overbar && !M_DEFINED(sym->type) && pass == 2) { - sym->type = DEFINED; - sym->val = vars_addr++; - nvars++; - } - return sym; /* return exact match */ - } /* match */ - - /* save best non-exact match; MACRO returns last defined n-x match! */ - if ((M_PSEUDO(sym->type)||M_EPSEUDO(sym->type)||M_MACRO(sym->type)) && - strncmp(name, sym->name, 3) == 0) - best = sym; - } /* while */ - - /* return best match (pseudo or macro) if any for lookups (not defns) */ - if (best && type == UNDEFINED) - return best; - - /* Must put symbol in table if index is negative. */ - ix = lx; /* insertion point */ - if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { - errorSymbol( &symbol_table_full, name, lexstart ); - exit( 1 ); - } - - for( rx = symbol_top; rx >= ix; rx-- ) - symtab[rx + 1] = symtab[rx]; - - symbol_top++; - - /* Enter the symbol as UNDEFINED with a value of zero. */ - sym = symtab + ix; - strcpy( sym->name, name ); - sym->type = UNDEFINED; - sym->val = 0; - sym->xref_count = 0; - if( xref && pass == 2 && sym->xref_index >= 0) - xreftab[sym->xref_index] = 0; - - if (overbar) - nvars++; - - return sym; -} /* lookup */ - -/* Function: compareSymbols */ -/* Synopsis: Used to presort the symbol table when starting assembler. */ -int compareSymbols( const void *a, const void *b ) -{ - return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); -} /* compareSymbols */ - -/* Function: evalSymbol */ -/* Synopsis: Get the pointer for the symbol table entry if exists. */ -/* If symbol doesn't exist, return a pointer to the undefined sym */ -SYM_T *evalSymbol() -{ - char name[SYMLEN]; - SYM_T *sym; - - sym = lookup( lexemeToName( name, lexstart, lexterm ), UNDEFINED); - - sym->xref_count++; /* Count the number of references to symbol. */ - - if( xref && pass == 2 && sym->xref_index >= 0) - { - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + sym->xref_count] = lineno; - } - - return( sym ); -} /* evalSymbol */ - - -/* Function: moveToEndOfLine */ -/* Synopsis: Move the parser input to the end of the current input line. */ -void moveToEndOfLine() -{ - while( !ISEND( line[cc] )) cc++; /* XXX wrong! will stop on a tab! */ - lexstart = cc; - lexterm = cc; - lexstartprev = lexstart; -} /* moveToEndOfLine */ - -/* frame the next token in "line" with lexstart and lexterm indicies */ -void -next(int op) { - char c; - - /* Save start column of previous lexeme for diagnostic messages. */ - lexstartprev = lexstart; - lextermprev = lexterm; - - c = line[cc]; - if (c == ' ') { - /* eat spaces */ - do { - c = line[++cc]; - } while (c == ' '); - if (op) /* looking for operators? */ - cc--; /* return one */ - } - - overbar = 0; - lexstart = cc; - c = line[cc]; - if( isalnum(c) || ISOVERBAR(c)) { - if (ISOVERBAR(c)) - overbar = 1; - do { - c = line[++cc]; - if (ISOVERBAR(c)) - overbar = 1; - } while (isalnum(c) || ISOVERBAR(c)); - } - else if(!ISDONE(c) || c == '\t') /* not end of line, or comment */ - cc++; /* advance past all punctuation */ - lexterm = cc; -} /* next */ - -BOOL isLexSymbol() -{ - int ix; - - /* XXX alpha within first 4? 3?? */ - for( ix = lexstart; ix < lexterm; ix++ ) - if(isalpha(line[ix])) - return TRUE; /* any position will do! */ - return FALSE; -} /* isLexSymbol */ - -/* - * from macro manual (F-36BP), p.18; - * - * "A macro-instruction definition consists of four parts; - * the pseudo-instruction _define_, the _macro instruction name_ - * amd _dummy symbol list,_ the _body_, and the pseudo-instruction - * _terminate_. Each part is followed by at least one tabulation or - * carriage return." - * - * and in the next paragraph; - * - * "The name is terminated by a _space_ or by a _tab_ or _cr_ - * if there is no dummy symbol list." - * - * This accepts tabs and/or a newline after define - * (but will accept a space), and only accepts spaces - * between macro and dummy names. - */ - -void -defineMacro() { - int lexstartsave; /* point to macro name */ - int index; /* point to error char */ - int error; /* error boolean */ - int i; - int count; - WORD32 length; - WORD32 value; - char termin[SYMLEN]; - char args[MAC_MAX_ARGS][SYMLEN]; /* macro & arg names */ - char body[MAC_MAX_LENGTH + 1]; - struct macdef *mdp; - SYM_T *sym; - - if (nrepeats) { - /* we can call readLine, so throw up hands now */ - errorLexeme( &define_in_repeat, lexstartprev ); - return; - } - - while (line[lexstart] == ' ' || line[lexstart] == '\t') - next(0); - - /* not a tab or space */ - if (ISEND(line[lexstart])) { /* newline or EOS? */ - /* crock; next token should invisibly skip over line boundaries? */ - readLine(); - next(0); - while (line[lexstart] == ' ' || line[lexstart] == '\t') - next(0); - } - - /* XXX pick up macro name out here */ - - count = 0; - index = 0; - error = FALSE; - lexstartsave = lexstart; - while (!ISDONE(line[lexstart]) && count < MAC_MAX_ARGS) { - if (!isalnum(line[lexstart]) && index == 0) - index = lexstart; /* error pointer */ - lexemeToName( args[count++], lexstart, lexterm ); - /* XXX error if NOT a comma (& not first dummy) ? */ - if (line[lexterm] == ',') - next(0); /* eat the comma */ - next(0); - if (line[lexstart] == ' ') - next(0); - } - if( count == 0 ) { /* No macro name. */ - errorMessage( &no_macro_name, lexstartsave ); - error = TRUE; - } - else if( index ) { /* Bad argument name. */ - errorMessage( &bad_dummy_arg, index ); - error = TRUE; - } - else if( mac_count >= MAC_TABLE_LENGTH ) { - errorMessage( ¯o_table_full, lexstartsave ); - error = TRUE; - } - else { - value = mac_count++; /* sym value is index into mac */ - defineSymbol( args[0], value, MACRO, lexstartsave ); - } - - for( length = 0;; ) { - readLine(); - if (end_of_input) - break; - next(0); - while (line[lexstart] == ' ' || line[lexstart] == '\t') - next(0); - - lexemeToName( termin, lexstart, lexterm ); /* just look at line? */ - if (strncmp( termin, "term", 4 ) == 0) - break; - - if (!error) { - int ll = strlen(line); - int allblank = FALSE; - - /* don't save blank lines! */ - for( i = 0; i < ll && allblank; i++ ) - if(!ISBLANK(line[i])) - allblank = FALSE; - - if (allblank) /* nothing but air? */ - continue; /* skip it! */ - - if ((length + ll + 1) >= MAC_MAX_LENGTH ) { - errorMessage (¯o_too_long, lexstart ); - error = TRUE; - continue; - } - - strcpy(body+length, line); - length += ll; - } - } /* for */ - if( error ) - return; - - mdp = calloc(1, sizeof(struct macdef) + length); - if (mdp == NULL) { - fprintf(stderr, "error allocating memory for macro definition\n"); - exit(1); - } - mac_defs[value] = mdp; - - strncpy(mdp->body, body, length); - mdp->body[length] = '\0'; - mdp->nargs = count - 1; - - /* - * save dummy names - * symbol slot 0 reserved for "r" symbol - * move SYM_T entries to macinv to allow recursion - */ - sym = mdp->args; - sym->type = DEFINED; - strcpy(sym->name, "R"); - sym->val = 0; - sym->xref_index = -1; /* ??? allow xref? */ - sym++; - - for (i = 1; i <= mdp->nargs; i++, sym++) { - sym->type = DEFINED; - strcpy(sym->name, args[i]); - sym->val = 0; - sym->xref_index = -1; /* don't xref!! */ - } -} /* defineMacro */ - -/* VARIABLES pseudo-op */ -void -variables() { - /* XXX error if "variables" already seen (in this pass) */ - /* XXX error if different address on pass 2 */ - if (pass == 2) - printLine( line, clc, 0, LINE_LOC ); - vars_addr = clc; - vars_end = clc = (clc + nvars) & ADDRESS_FIELD; - if (pass == 2) - printLine( line, clc, 0, LINE_LOC); -} - -/* TEXT pseudo-op */ -void -text(void) -{ - char delim; - WORD32 w; - int count; - int ccase; - /* XXX error in repeat!! */ - do { - if (cc == maxcc) { - /* XXX EOL before delim found!!! */ - fprintf(stderr, "FIX ME!\n"); - return; - } - delim = line[cc++]; - } while (delim == ' '); /* others? NL */ - - w = count = 0; - ccase = LC; - for (;;) { - int c = nextfiodec(&ccase, delim); - if (c == -1) - break; - w |= c << ((2-count)*6); - if (++count == 3) { - punchOutObject(clc, w); /* punch it! */ - incrementClc(); - count = w = 0; - } - } - if (count > 0) { - punchOutObject(clc, w); /* punch remainder */ - incrementClc(); - } -} - -/* CONSTANTS pseudo-op */ -void -constants(void) { - int i; - - /* XXX illegal inside macro (curmacro != NULL) */ - - if (pass == 1) { - lit_loc[nconst] = clc; - - /* just use addition?! */ - for (i = 0; i < lit_count[nconst]; i++) - incrementClc(); - - nconst++; - return; - } - - /* pass 2: */ - /* XXX complain if clc != lit_base[nconst]? */ - - for (i = 0; i < lit_count[nconst]; i++) { - if (i < nlit) - punchOutObject( clc, litter[i] & 0777777); /* punch it! */ - incrementClc(); - } - - nconst++; - nlit = 0; /* litter[] now empty */ -} /* constants */ - - -/* process pseudo-ops - * return FALSE if line scan should end (no longer used) - */ -BOOL pseudo( PSEUDO_T val ) -{ - int count; - int repeatstart; - - switch( (PSEUDO_T) val ) { - case CONSTANTS: - next(0); /* Skip symbol */ - constants(); - break; - - case VARIABLES: - next(0); /* Skip symbol */ - variables(); - break; - - case DEFINE: - next(0); /* Skip symbol */ - defineMacro(); - return FALSE; - break; - - case REPEAT: - next(0); /* Skip symbol */ - - /* NOTE!! constant followed by SPACE picked up as expression!! */ - count = getExprs() & ADDRESS_FIELD; - /* XXX error if sign bit set? */ - - /* allow comma, but do not require */ - if( line[lexstart] == ',') - next(0); - - nrepeats++; - repeatstart = lexstart; /* save line start */ - while (count-- > 0) { - cc = repeatstart; /* reset input pointer */ - processLine(); /* recurse! */ - } - cc = maxcc; - nrepeats--; - - return FALSE; - break; - - case START: - next(0); /* Skip symbol */ - /* XXX illegal in macro or repeat */ - flushLoader(); - if (!ISDONE(line[lexstart])) { - if (line[lexstart] == ' ') - next(0); - start_addr = getExprs() & ADDRESS_FIELD; - next(0); - printLine( line, 0, start_addr, LINE_VAL ); - /* MACRO punches 4" of leader */ - punchTriplet(JMP | start_addr); - /* MACRO punches 24" of leader? */ - } - /* - * handle multiple tapes concatenated into one file!! - * have command line option?? treat "start" as EOF?? - */ - list_title_set = FALSE; - return FALSE; - - case TEXT: - /* NOTE!! no next()! */ - text(); - break; - - case NOINPUT: - next(0); /* Skip symbol */ - noinput = TRUE; - break; - - case EXPUNGE: - next(0); /* Skip symbol */ - if (pass == 1) - init_symtab(); - break; - - default: - break; - } /* end switch for pseudo-ops */ - return TRUE; /* keep scanning */ -} /* pseudo */ - - -/* Function: errorLexeme */ -/* Synopsis: Display an error message using the current lexical element. */ -void errorLexeme( EMSG_T *mesg, WORD32 col ) -{ - char name[SYMLEN]; - - errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); -} /* errorLexeme */ - - -/* Function: errorSymbol */ -/* Synopsis: Display an error message with a given string. */ -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) -{ - char linecol[12]; - char *s; - - if( pass == 2 ) - { - s = ( name == NULL ) ? "" : name ; - errors++; - sprintf( linecol, ":%d:%d", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", - filename, linecol, mesg->file, s, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorSymbol */ - - -/* Function: errorMessage */ -/* Synopsis: Display an error message without a name argument. */ -void errorMessage( EMSG_T *mesg, WORD32 col ) -{ - char linecol[12]; - - if( pass == 2 ) - { - errors++; - sprintf( linecol, ":%d:%d", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", - filename, linecol, mesg->file, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorMessage */ - -/* Function: saveError */ -/* Synopsis: Save the current error in a list so it may displayed after the */ -/* the current line is printed. */ -void saveError( char *mesg, WORD32 col ) -{ - if( save_error_count < DIM( error_list )) - { - error_list[save_error_count].mesg = mesg; - error_list[save_error_count].col = col; - save_error_count++; - } - error_in_line = TRUE; - - if( listed ) - printErrorMessages(); -} /* saveError */ - -/* create a "symbol punch" for DDT */ -/* MUST be called after object file closed; we reuse the FILE*! */ - -void -dump_symbols(void) { - int ix; - WORD32 addr; - - objectfile = fopen( sympathname, "wb" ); - if (!objectfile) { - perror(sympathname); - return; - } - - punchLeader(0); - punchLoader(); - punchLeader(5); - - /* XXX fudge addr -- get count, and subtract 2N from 07750? */ - addr = 05000; - - for( ix = 0; ix < symbol_top; ix++ ) { - int i, type; - WORD32 name; - - type = symtab[ix].type; - if (M_FIXED(type) || M_PSEUDO(type) || M_MACRO(type)) - continue; - - name = 0; - for (i = 0; i < 3; i++) { - char c; - - c = symtab[ix].name[i]; - /* XXX leave on NUL? */ - - c = ascii_to_fiodec[tolower(c) & 0177]; - /* XXX check for BAD entries? */ - - /* XXX OR in val<<(3-i)*6?? */ - name <<= 6; - name |= c & CHARBITS; - } - punchLocObject(addr++, permute(name)); - punchLocObject(addr++, symtab[ix].val); - } - flushLoader(); - punchTriplet( JMP ); /* ??? */ - punchLeader(0); - fclose(objectfile); -} +/* + * $Id: macro1.c,v 1.74 2003/10/23 23:29:17 phil Exp $ + * + * TODO: + * have flex() use nextfiodec()?? (what if legal in repeat???) + * "flexx" should give right justified result??? + * squawk if nextfiodec called in a repeat w/ a delim? + * + * forbid variables/constants in macros + * forbid text in repeat?? + * forbid start in repeat or macro + * use same error TLA's as MACRO??? + * IPA error for overbar on LHS of = + * variables returns value?? + * + * macro addressing: labels defined during macro are local use only???? + * spacewar expects this??? (is it wrong?) + * + * self-feeding lines: \n legal anywhere \t is + * read next token into "token" buffer -- avoid saving "line"? + * remove crocks from "define" + * list title (first line of file) should not be parsed as source? + * incorrect listing for bare "start" + * list only 4 digits for address column + * + * other; + * note variables in symbol dump, xref? + * no "permenant" symbols; flush -p? rename .ini? + * keep seperate macro/pseudo table? + * verify bad input(?) detected + * implement dimension pseudo? + * remove crocks for '/' and ','? + */ + +/* + * Program: MACRO1 + * File: macro1.c + * Author: Gary A. Messenbrink (macro8) + * MACRO7 modifications: Bob Supnik + * MACRO1 modifications: Bob Supnik + * slashed to be more like real MACRO like by Phil Budne + * + * Purpose: A 2 pass PDP-1 assembler + * + * NAME + * macro1 - a PDP-1 assembler. + * + * SYNOPSIS: + * macro1 [ -d -p -m -r -s -x ] inputfile inputfile... + * + * DESCRIPTION + * This is a cross-assembler to for PDP-1 assembly language programs. + * It will produce an output file in rim format only. + * A listing file is always produced and with an optional symbol table + * and/or a symbol cross-reference (concordance). The permanent symbol + * table can be output in a form that may be read back in so a customized + * permanent symbol table can be produced. Any detected errors are output + * to a separate file giving the filename in which they were detected + * along with the line number, column number and error message as well as + * marking the error in the listing file. + * The following file name extensions are used: + * .mac source code (input) + * .lst assembly listing (output) + * .rim assembly output in DEC's rim format (output) + * .prm permanent symbol table in form suitable for reading after + * the EXPUNGE pseudo-op. + * .sym "symbol punch" tape (for DDT, or reloading into macro) + * + * OPTIONS + * -d Dump the symbol table at end of assembly + * -p Generate a file with the permanent symbols in it. + * (To get the current symbol table, assemble a file than has only + * START in it.) + * -x Generate a cross-reference (concordance) of user symbols. + * -r Output a tape using only RIM format (else output block loader) + * -s Output a symbol dump tape (loader + loader blocks) + * -S file + * Read a symbol tape back in + * + * DIAGNOSTICS + * Assembler error diagnostics are output to an error file and inserted + * in the listing file. Each line in the error file has the form + * + * :: : error: at Loc = + * + * An example error message is: + * + * bintst.7:17:9 : error: undefined symbol "UNDEF" at Loc = 07616 + * + * The error diagnostics put in the listing start with a two character + * error code (if appropriate) and a short message. A carat '^' is + * placed under the item in error if appropriate. + * An example error message is: + * + * 17 07616 3000 DAC UNDEF + * UD undefined ^ + * 18 07617 1777 TAD I DUMMY + * + * Undefined symbols are marked in the symbol table listing by prepending + * a '?' to the symbol. Redefined symbols are marked in the symbol table + * listing by prepending a '#' to the symbol. Examples are: + * + * #REDEF 04567 + * SWITCH 07612 + * ?UNDEF 00000 + * + * Refer to the code for the diagnostic messages generated. + * + * REFERENCES: + * This assembler is based on the pal assember by: + * Douglas Jones and + * Rich Coon + * + * COPYRIGHT NOTICE: + * This is free software. There is no fee for using it. You may make + * any changes that you wish and also give it away. If you can make + * a commercial product out of it, fine, but do not put any limits on + * the purchaser's right to do the same. If you improve it or fix any + * bugs, it would be nice if you told me and offered me a copy of the + * new version. + * + * + * Amendments Record: + * Version Date by Comments + * ------- ------- --- --------------------------------------------------- + * v1.0 12Apr96 GAM Original + * v1.1 18Nov96 GAM Permanent symbol table initialization error. + * v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. + * v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). + * v1.4 29Nov96 GAM Fixed bug in checksum generation. + * v2.1 08Dec96 GAM Added concordance processing (cross reference). + * v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). + * v2.3 2Feb97 GAM Fixed paging problem in cross reference output. + * v3.0 14Feb97 RMS MACRO8X features. + * 8Mar97 RMS MACRO7 released w/ sim8swre + * 13Mar97 RMS MACRO1 released w/ lispswre + * 28Nov01 RMS MACRO1 released w/ simtools + * 5Mar03 DP MACRO1 released w/ ddt1 + * 2003 PLB major reworking, assembles MACRO, DDT + */ + + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +/*#define SYMSIG 4 /* EXP: significant chars in a symbol */ +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ + +#define MAX_LITERALS 1000 +#define MAX_CONSTANTS 10 /* max number of "constants" blocks */ + +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 0007777 +#define INDIRECT_BIT 0010000 +#define OP_CODE 0760000 + +#define CONCISE_LC 072 +#define CONCISE_UC 074 + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +#define ISBLANK(c) ((c==' ') || (c=='\f')) +#define ISEND(c) ((c=='\0')|| (c=='\n') || (c == '\t')) +#define ISDONE(c) ((c=='/') || ISEND(c)) + +#define ISOVERBAR(c) (c == '\\' || c == '~') + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_DEFINED(s) (((s) & DEFINED) == DEFINED) +#define M_DUPLICATE(s) (((s) & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) (((s) & FIXED) == FIXED) +#define M_LABEL(s) (((s) & LABEL) == LABEL) +#define M_PSEUDO(s) (((s) & PSEUDO) == PSEUDO) +#define M_EPSEUDO(s) (((s) & EPSEUDO) == EPSEUDO) +#define M_MACRO(s) (((s) & MACRO) == MACRO) +#define M_NOTRDEF(s) (((s) & NOTRDEF) != 0) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL, LINE_LOC +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + EPSEUDO = 0200 | FIXED | DEFINED, + MACRO = 0400 | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t { + DECIMAL, + DEFINE, + FLEX, + CONSTANTS, + OCTAL, + REPEAT, + START, + CHAR, + VARIABLES, + TEXT, + NOINPUT, + EXPUNGE +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int compareSymbols( const void *a, const void *b ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T eval( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +SYM_T getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +WORD32 literal( WORD32 value ); +BOOL isLexSymbol(); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name, int type ); +void moveToEndOfLine( void ); +void next(int); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudo( PSEUDO_T val ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchLoader( void ); +void flushLoader( void ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +void topOfForm( char *title, char *sub_title ); +void constants(void); +void variables(void); +void eob(void); +void dump_symbols(void); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup */ +SYM_T pseudos[] = +{ + { PSEUDO, "consta", CONSTANTS }, + { PSEUDO, "define", DEFINE }, /* Define macro. */ + { PSEUDO, "repeat", REPEAT }, + { PSEUDO, "start", START }, /* Set starting address. */ + { PSEUDO, "variab", VARIABLES }, + { PSEUDO, "text", TEXT }, + { PSEUDO, "noinpu", NOINPUT }, + { PSEUDO, "expung", EXPUNGE }, +/* the following can appear in expressions: */ + { EPSEUDO, "charac", CHAR }, + { EPSEUDO, "decima", DECIMAL }, /* base 10. */ + { EPSEUDO, "flexo", FLEX }, + { EPSEUDO, "octal", OCTAL }, /* Read literal constants in base 8. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +#define DIO 0320000 +#define JMP 0600000 +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { DEFFIX, "and", 0020000 }, + { DEFFIX, "ior", 0040000 }, + { DEFFIX, "xor", 0060000 }, + { DEFFIX, "xct", 0100000 }, + { DEFFIX, "lac", 0200000 }, + { DEFFIX, "lio", 0220000 }, + { DEFFIX, "dac", 0240000 }, + { DEFFIX, "dap", 0260000 }, + { DEFFIX, "dip", 0300000 }, + { DEFFIX, "dio", 0320000 }, + { DEFFIX, "dzm", 0340000 }, + { DEFFIX, "add", 0400000 }, + { DEFFIX, "sub", 0420000 }, + { DEFFIX, "idx", 0440000 }, + { DEFFIX, "isp", 0460000 }, + { DEFFIX, "sad", 0500000 }, + { DEFFIX, "sas", 0520000 }, + { DEFFIX, "mul", 0540000 }, + { DEFFIX, "mus", 0540000 }, /* for spacewar */ + { DEFFIX, "div", 0560000 }, + { DEFFIX, "dis", 0560000 }, /* for spacewar */ + { DEFFIX, "jmp", 0600000 }, + { DEFFIX, "jsp", 0620000 }, + { DEFFIX, "skip", 0640000 }, /* for spacewar */ + { DEFFIX, "cal", 0160000 }, + { DEFFIX, "jda", 0170000 }, + { DEFFIX, "i", 0010000 }, + { DEFFIX, "skp", 0640000 }, + { DEFFIX, "law", 0700000 }, + { DEFFIX, "iot", 0720000 }, + { DEFFIX, "opr", 0760000 }, + { DEFFIX, "nop", 0760000 }, + /* Shift Instructions */ + { DEFFIX, "ral", 0661000 }, + { DEFFIX, "ril", 0662000 }, + { DEFFIX, "rcl", 0663000 }, + { DEFFIX, "sal", 0665000 }, + { DEFFIX, "sil", 0666000 }, + { DEFFIX, "scl", 0667000 }, + { DEFFIX, "rar", 0671000 }, + { DEFFIX, "rir", 0672000 }, + { DEFFIX, "rcr", 0673000 }, + { DEFFIX, "sar", 0675000 }, + { DEFFIX, "sir", 0676000 }, + { DEFFIX, "scr", 0677000 }, + { DEFFIX, "1s", 0000001 }, + { DEFFIX, "2s", 0000003 }, + { DEFFIX, "3s", 0000007 }, + { DEFFIX, "4s", 0000017 }, + { DEFFIX, "5s", 0000037 }, + { DEFFIX, "6s", 0000077 }, + { DEFFIX, "7s", 0000177 }, + { DEFFIX, "8s", 0000377 }, + { DEFFIX, "9s", 0000777 }, + /* Skip Microinstructions */ + { DEFFIX, "sza", 0640100 }, + { DEFFIX, "spa", 0640200 }, + { DEFFIX, "sma", 0640400 }, + { DEFFIX, "szo", 0641000 }, + { DEFFIX, "spi", 0642000 }, + { DEFFIX, "szs", 0640000 }, + { DEFFIX, "szf", 0640000 }, + /*{ DEFFIX, "clo", 0651600 },*/ + + /* Operate Microinstructions */ + { DEFFIX, "clf", 0760000 }, + { DEFFIX, "stf", 0760010 }, + { DEFFIX, "cla", 0760200 }, + /*{ DEFFIX, "lap", 0760300 },*/ + { DEFFIX, "hlt", 0760400 }, + { DEFFIX, "xx", 0760400 }, + { DEFFIX, "cma", 0761000 }, + { DEFFIX, "clc", 0761200 }, + { DEFFIX, "lat", 0762200 }, + { DEFFIX, "cli", 0764000 }, + /* IOT's */ + /*{ DEFFIX, "ioh", 0730000 },*/ + { DEFFIX, "rpa", 0730001 }, + { DEFFIX, "rpb", 0730002 }, + { DEFFIX, "rrb", 0720030 }, + { DEFFIX, "ppa", 0730005 }, + { DEFFIX, "ppb", 0730006 }, + { DEFFIX, "tyo", 0730003 }, + { DEFFIX, "tyi", 0720004 }, + { DEFFIX, "dpy", 0730007 }, /* for spacewar, munching squares! */ + { DEFFIX, "lsm", 0720054 }, + { DEFFIX, "esm", 0720055 }, + { DEFFIX, "cbs", 0720056 }, + { DEFFIX, "lem", 0720074 }, + { DEFFIX, "eem", 0724074 }, + { DEFFIX, "cks", 0720033 }, +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +#define LOADERBASE 07751 + +/* make it relocatable (DDT expects it at 7751) */ +#define LOADER_IN LOADERBASE +#define LOADER_B (LOADERBASE+06) +#define LOADER_A (LOADERBASE+07) +#define LOADER_CK (LOADERBASE+025) +#define LOADER_EN1 (LOADERBASE+026) + +WORD32 loader[] = { + 0730002, /* in, rpb */ + 0320000+LOADER_A, /* dio a */ + 0100000+LOADER_A, /* xct a */ + 0320000+LOADER_CK, /* dio ck */ + 0730002, /* rpb */ + 0320000+LOADER_EN1, /* dio en1 */ + 0730002, /* b, rpb */ + 0000000, /* a, xx */ + 0210000+LOADER_A, /* lac i a */ + 0400000+LOADER_CK, /* add ck */ + 0240000+LOADER_CK, /* dac ck */ + 0440000+LOADER_A, /* idx a */ + 0520000+LOADER_EN1, /* sas en1 */ + 0600000+LOADER_B, /* jmp b */ + 0200000+LOADER_CK, /* lac ck */ + 0400000+LOADER_EN1, /* add en1 */ + 0730002, /* rpb */ + 0320000+LOADER_CK, /* dio ck */ + 0520000+LOADER_CK, /* sas ck */ + 0760400, /* hlt */ + 0600000+LOADER_IN /* jmp in */ + /* ck, 0 */ + /* en1, 0 */ +}; + +#define LOADERBUFSIZE 0100 /* <=0100, power of 2*/ +#define LOADERBUFMASK (LOADERBUFSIZE-1) /* for block alignment */ + +WORD32 loaderbuf[LOADERBUFSIZE]; +WORD32 loaderbufcount; +WORD32 loaderbufstart; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* VR Value Required */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T value_required = { "VR value required", "value required" }; +EMSG_T literal_gen_off = { "lit generation off", + "literal generation disabled" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; +EMSG_T define_in_repeat = { "define in a repeat", "Define in a repeat" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char sympathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +WORD32 mac_count; /* Total macros defined. */ + +/* + * malloced macro bodies, indexed by sym->val dummies are evaluated at + * invocation time, and value saved in "args"; if recursive macros are + * desired (without conditionals, how would you escape?), keep a name + * list here and move symbols to "macinv" + */ +struct macdef { + int nargs; /* number of args */ + SYM_T args[MAC_MAX_ARGS+1]; /* symbol for each and one for "r" */ + char body[1]; /* malloc'ed accordingly */ +} *mac_defs[MAC_TABLE_LENGTH]; + +struct macinv { /* current macro invocation */ + char mac_line[LINELEN]; /* Saved macro invocation line. */ + WORD32 mac_cc; /* Saved cc after macro invocation. */ + char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ + struct macdef *defn; /* pointer to definition for dummies */ + struct macinv *prev; /* previous invocation in stack */ +} *curmacro; /* macro stack */ + +int nrepeats; /* count of nested repeats */ + +int list_lineno; +int list_pageno; +char list_title[LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 clc; /* Location counter */ +BOOL end_of_input; /* End of all input files. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +int lexstartprev; /* Where previous lexeme started. */ +int lextermprev; /* Where previous lexeme ended. */ +int lexstart; /* Index of current lexeme on line. */ +int lexterm; /* Index of character after current lexeme. */ +int overbar; /* next saw an overbar in last token */ + +int nconst; /* number of "constants" blocks */ +int lit_count[MAX_CONSTANTS]; /* # of lits in each block in pass 1 */ +WORD32 lit_loc[MAX_CONSTANTS]; /* Base of literal blocks */ + +int noinput; /* don't punch loader */ + +int nvars; /* number of variables */ +WORD32 vars_addr; /* address of "variables" */ +WORD32 vars_end; /* end of "variables" */ + +/* pass 2 only; */ +int nlit; /* number of literals in litter[] */ +WORD32 litter[MAX_LITERALS]; /* literals */ + +WORD32 maxcc; /* Current line length. */ +BOOL nomac_exp; /* No macro expansion */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +BOOL rim_mode; /* RIM mode output. */ +BOOL sym_dump; /* punch symbol tape */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +WORD32 start_addr; /* Saved start address. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + +/* initial data from SIMH v3.0 pdp1_stddev.c (different encoding of UC/LC) */ +#define UC 0100 /* Upper case */ +#define LC 0200 +#define CHARBITS 077 +#define BC LC|UC /* both case bits */ +#define BAD 014 /* unused concise code */ + +unsigned char ascii_to_fiodec[128] = { + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BC|075, BC|036, BAD, BAD, BAD, BC|077, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, + BC|000, UC|005, UC|001, UC|004, BAD, BAD, UC|006, UC|002, + LC|057, LC|055, UC|073, UC|054, LC|033, LC|054, LC|073, LC|021, + LC|020, LC|001, LC|002, LC|003, LC|004, LC|005, LC|006, LC|007, + LC|010, LC|011, BAD, BAD, UC|007, UC|033, UC|010, UC|021, + LC|040, UC|061, UC|062, UC|063, UC|064, UC|065, UC|066, UC|067, + UC|070, UC|071, UC|041, UC|042, UC|043, UC|044, UC|045, UC|046, + UC|047, UC|050, UC|051, UC|022, UC|023, UC|024, UC|025, UC|026, + UC|027, UC|030, UC|031, UC|057, LC|056, UC|055, UC|011, UC|040, + UC|020, LC|061, LC|062, LC|063, LC|064, LC|065, LC|066, LC|067, + LC|070, LC|071, LC|041, LC|042, LC|043, LC|044, LC|045, LC|046, + LC|047, LC|050, LC|051, LC|022, LC|023, LC|024, LC|025, LC|026, + LC|027, LC|030, LC|031, BAD, UC|056, BAD, UC|003, BC|075 +}; + +/* for symbol punch tape conversion only!! */ +char fiodec_to_ascii[64] = { + 0, '1', '2', '3', '4', '5', '6', '7', + '8', '9', 0, 0, 0, 0, 0, 0, + '0', 0, 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', 0, 0, 0, 0, 0, 0, + 0, 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 0, 0, 0, 0, 0, 0, + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 0, 0, 0, 0, 0, 0 }; + +/* used at startup & for expunge */ +void +init_symtab(void) { + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; +} + +/* Function: main */ +/* Synopsis: Starting point. Controls order of assembly. */ +int +main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + print_permanent_symbols = FALSE; + nomac_exp = TRUE; + rim_mode = FALSE; /* default to loader tapes */ + sym_dump = FALSE; + noinput = FALSE; + + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + + /* init symbol table before processing arguments, so we can + * load symbol punch tapes on the fly + */ + + /* + * Setup the error file in case symbol table overflows while + * installing the permanent symbols. + */ + errorfile = stderr; + pass = 0; /* required for symbol table init */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + init_symtab(); + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudos ); ix++ ) + defineSymbol( pseudos[ix].name, pseudos[ix].val, pseudos[ix].type, 0 ); + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type, 0 ); + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Do pass one of the assembly */ + pass = 1; + onePass(); + errors_pass_1 = errors; + + /* Set up for pass two */ + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + /* XXX punch title into tape! */ + punchLeader( 0 ); + if (!rim_mode) { + punchLoader(); + punchLeader(5); + } + + if (nlit > 0) + constants(); /* implied "constants"? */ + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) { + /* Get the amount of space that will be required for the concordance */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + } + /* Allocate & clear the necessary space. */ + xreftab = (WORD32 *) calloc( space, sizeof( WORD32 )); + } + pass = 2; + onePass(); + + objectfile = objectsave; + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) { + fprintf( listfile, "\n %s %s %s\n", s_no, s_errors, s_detected ); + } + else { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + printSymbolTable(); + + if( print_permanent_symbols ) + printPermanentSymbolTable(); + + if( xref ) + printCrossReference(); + + fclose( objectfile ); + fclose( listfile ); + if( errors == 0 && errors_pass_1 == 0 ) { + /* after closing objectfile -- we reuse the FILE *!! */ + if (sym_dump) + dump_symbols(); + } + else + remove( objectpathname ); + + return( errors != 0 ); +} /* main() */ + +/* read a word from a binary punch file */ +WORD32 +getw(FILE *f) +{ + int i, c; + WORD32 w; + + w = 0; + for (i = 0; i < 3;) { + c = getc(f); + if (c == -1) + return -1; + if (c & 0200) { /* ignore if ch8 not punched */ + w <<= 6; + w |= c & 077; + i++; + } + } + return w; +} + +/* + * "permute zone bits" like MACRO does for proper sorting + * (see routine "per" in MACRO) -- it's what DDT expects + * + * it's it's own inverse! + */ + +WORD32 +permute(WORD32 name) +{ + WORD32 temp; + + temp = name & 0202020; /* get zone bits */ + temp = ((temp << 1) & 0777777) | ((temp >> 17) & 1); /* rotate left */ + name ^= temp; /* flip zone bits */ + name ^= 0400000; /* toggle sign */ + return name; +} + +/* add a symbol from a "symbol punch" tape */ +void +addsym(WORD32 sym, WORD32 val) +{ + char name[4]; + + sym = permute(sym); + name[0] = fiodec_to_ascii[(sym >>12) & 077]; + name[1] = fiodec_to_ascii[(sym >> 6) & 077]; + name[2] = fiodec_to_ascii[sym & 077]; + name[3] = '\0'; + defineSymbol( name, val, LABEL, 0); +} + +void +read_symbols(char *fname) +{ + FILE *f; + + f = fopen(fname, "rb"); + if (!f) { + perror(fname); + exit(1); + } + + /* skip loader */ + for (;;) { + WORD32 w; + + w = getw(f); + if (w == -1) + goto err; /* XXX complain? */ + if ((w & OP_CODE) == JMP) + break; + if ((w & OP_CODE) != DIO) + goto err; /* XXX complain? */ + w = getw(f); + if (w == -1) + goto err; /* XXX complain? */ + } + + + /* XXX should push block reader down into a co-routine */ + for (;;) { + WORD32 start, end, sum; + + start = getw(f); + if ((start & OP_CODE) == JMP) { + fclose(f); + return; + } + + if (start == -1 || (start & OP_CODE) != DIO) + goto err; + + end = getw(f); + if (end == -1 || (end & OP_CODE) != DIO) + goto err; /* XXX complain? */ + + sum = start + end; + while (start < end) { + WORD32 sym, val; + sym = getw(f); + if (sym == -1) + goto err; + sum += sym; + start++; + /* XXX handle block boundaries? */ + if (start >= end) + goto err; + val = getw(f); + if (val == -1) + goto err; + /*printf("%06o %06o\n", sym, val);*/ + addsym(sym, val); + sum += val; + start++; + } + start = getw(f); /* eat checksum XXX verify? */ + if (start == -1) + goto err; + /* roll over all the overflows at once */ + if (sum & ~0777777) { + sum = (sum & 0777777) + (sum >> 18); + if (sum & 01000000) /* one more time */ + sum++; + } + if (start != sum) + goto err; + } +err: + fprintf(stderr, "error reading symbol file %s\n", fname); + exit(1); +} + +/* Function: getArgs */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ) + { + if( argv[ix][0] == '-' ) + { + char *switches = argv[ix++]; + for( jx = 1; switches[jx] != 0; jx++ ) + { + switch( switches[jx] ) + { + case 'd': + symtab_print = TRUE; + break; + + case 'r': + rim_mode = TRUE; /* punch pure rim-mode tapes */ + break; + + case 's': + sym_dump = TRUE; + break; + + case 'm': + nomac_exp = FALSE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + case 'S': + if (ix <= argc) + read_symbols(argv[ix++]); + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- output macro expansions\n" ); + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -r -- output RIM format file\n" ); + fprintf( stderr, " -s -- output symbol punch tape to file\n" ); + fprintf( stderr, " -S file -- read symbol punch tape\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, ".rim"); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + strncpy( sympathname, pathname, jx ); + sympathname[jx] = '\0'; + strcat( sympathname, ".sym" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + pathname[1] = '\\'; /* MS-DOS style pathname */ + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + jx--; + strcpy( filename, &pathname[jx + 1] ); +} /* getArgs() */ + + +int +invokeMacro(int index) +{ + struct macinv *mip; + struct macdef *mdp; + int jx; + + mdp = mac_defs[index]; + if (mdp == NULL || mdp->body[0] == '\0') + return 0; + + /* Find arguments. */ + while (ISBLANK(line[lexstart])) + next(0); + + mip = calloc(1, sizeof(struct macinv)); + if (!mip) { + fprintf(stderr, "could not allocate memory for macro invocation\n"); + exit(1); + } + mip->defn = mdp; + + /* evaluate args, saving values in SYM_T entries in defn. + * (cannot have recursive macros) + */ + mdp->args[0].val = clc; /* r is location at start */ + for( jx = 1; !ISDONE(line[lexstart]) && jx <= MAC_MAX_ARGS; ) { + WORD32 val; + + next(0); + if (ISDONE(line[lexstart])) + break; + + if (line[lexstart] == ',') + next(0); + + while( ISBLANK( line[lexstart] )) + next(0); + + if (ISDONE(line[lexstart])) + break; + + val = getExprs(); + + /* ignore excess values silently? */ + if (jx <= mdp->nargs) + mdp->args[jx].val = val; + jx++; + } /* end for */ + + /* XXX complain if too few actuals? -- nah */ + while (jx <= mdp->nargs) + mdp->args[jx++].val = 0; + + strcpy(mip->mac_line, line); /* save line */ + mip->mac_cc = cc; /* save position in line */ + mip->mac_ptr = mdp->body; + mip->prev = curmacro; /* push the old entry */ + curmacro = mip; /* step up to the plate! */ + return 1; +} + +/* process input; used by onePass and repeat */ +void +processLine() { + if (!list_title_set) { + char *cp; + + /* assert(sizeof(title) >= sizeof(line)); */ + strcpy(list_title, line); + + if ((cp = strchr(list_title, '\n'))) + *cp = '\0'; + + if (list_title[0]) { + list_title_set = TRUE; + fprintf(stderr, "%s - pass %d\n", list_title, pass ); + /* XXX punch title into tape banner (until an '@' seen) */ + } + return; + } + + for (;;) { + int jx; + SYM_T evalue; + + next(0); + if( end_of_input ) + return; + + if( ISEND( line[lexstart] )) { + if (line[lexstart] != '\t') + return; + continue; + } + if (line[lexstart] == '/') /* comment? */ + return; /* done */ + + /* look ahead for 'exp/' */ + /* skip until whitespace or terminator */ + for( jx = lexstart; jx < maxcc; jx++ ) + if( ISBLANK(line[jx]) || ISDONE(line[jx])) + break; + if( line[jx] == '/') { /* EXP/ set location */ + WORD32 newclc; + + newclc = getExprs(); + + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + clc = newclc; + + printLine( line, newclc, 0, LINE_LOC ); + cc = jx + 1; + next(0); /* discard slash */ + continue; + } + + switch( line[lexterm] ) { + case ',': + if( isLexSymbol()) { + WORD32 val; + SYM_T *sym; + char name[SYMLEN]; + + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup(lexemeToName(name, lexstart, lexterm), UNDEFINED); + + if (curmacro) { + /* relative during macro expansion!! */ + val = clc - curmacro->defn->args[0].val; + } + else + val = clc; + + if( M_DEFINED( sym->type )) { + if( sym->val != val && pass == 2 ) + errorSymbol( &duplicate_label, sym->name, lexstart ); + sym->type |= DUPLICATE; /* XXX never used! */ + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, val, LABEL ); + } + else if (isdigit(line[lexstart])) { /* constant, */ + int i; + WORD32 val = 0; + + for( i = lexstart; i < lexterm; i++ ) { + if( isdigit( line[i] )) { + int digit; + digit = line[i] - '0'; + if( digit >= radix ) { + errorLexeme( &number_not_radix, i ); + val = 0; + break; + } + val = val * radix + digit; + } + else { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + break; + } + } + if (i == lexterm) { + if( clc != val && pass == 2 ) + errorLexeme( &duplicate_label, lexstart); /* XXX */ + } + } + else + errorLexeme( &label_syntax, lexstart ); + next(0); /* skip comma */ + continue; + + case '=': + if( isLexSymbol()) { + WORD32 start, term, val; + + start = lexstart; + term = lexterm; + next(0); /* skip symbol */ + next(0); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else { + errorLexeme( &symbol_syntax, lexstartprev ); + next(0); /* skip symbol */ + next(0); /* skip trailing = */ + getExprs(); /* skip expression */ + } + continue; + } /* switch on terminator */ + + if( isLexSymbol()) { + SYM_T *sym; + WORD32 val; + + sym = evalSymbol(); + val = sym->val; + if( M_MACRO(sym->type)) { + if (!invokeMacro(val)) + next(0); /* bad defn? or body is empty! */ + continue; + } /* macro invocation */ + else if( M_PSEUDO(sym->type)) { /* NO EPSEUDOs */ + pseudo( (PSEUDO_T)val & 0777777 ); + continue; + } /* pseudo */ + } /* macro, or non-char pseudo */ + + evalue = getExpr(); + if (evalue.type != PSEUDO) { /* not a bare pseudo-op? */ + if (line[lexstart] == ',') { /* EXP, */ + if(evalue.val != clc && pass == 2 ) + errorLexeme( &duplicate_label, lexstart); /* XXX */ + } + else if (line[lexstart] == '/') { /* EXP/ */ + clc = evalue.val; + printLine( line, clc, 0, LINE_LOC ); + next(0); + } + else { + punchOutObject( clc, evalue.val & 0777777); /* punch it! */ + incrementClc(); + } + } + } /* forever */ +} + +/* Function: onePass */ +/* Synopsis: Do one assembly pass. */ +void onePass() { + int ix; + + clc = 4; /* Default location is 4 */ + start_addr = 0; /* No starting address. */ + nconst = 0; /* No constant blocks seen */ + nvars = 0; /* No variables seen */ + + while (curmacro) { /* pop macro stack */ + struct macinv *mp; + + mp = curmacro->prev; + free(curmacro); + curmacro = mp; + } + + for( ix = 0; ix < mac_count; ix++) { + if (mac_defs[ix]) + free( mac_defs[ix] ); + mac_defs[ix] = NULL; + } + mac_count = 0; /* No macros defined. */ + + listed = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + end_of_input = FALSE; + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + + for (;;) { + readLine(); + if (end_of_input) { + eob(); + fclose( infile ); + return; + } + processLine(); + } /* forever */ +} /* onePass */ + + +/* Function: getExprs */ +/* Synopsys: gutted like a fish */ +WORD32 getExprs() +{ + SYM_T sym; + + sym = getExpr(); + if (sym.type == PSEUDO) + errorMessage( &value_required, lexstart ); /* XXX wrong pointer? */ + + return sym.val & 0777777; +} /* getExprs */ + + +SYM_T getExpr() +{ + SYM_T sym; + + sym = eval(); + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + + for (;;) { + int space; + /* + * falling out of switch breaks loop and returns from routine + * so if you want to keep going, you must "continue"!! + */ + space = FALSE; + switch( line[lexstart] ) { + case ' ': + space = TRUE; + /* fall */ + case '+': /* add */ + next(1); /* skip operator */ + if (space && ISEND(line[lexstart])) /* tollerate a trailing space */ + return sym; + sym.val += eval().val; /* XXX look at type? */ + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + + case '-': /* subtract */ + next(1); /* skip over the operator */ + sym.val += eval().val ^ 0777777; /* XXX look at type? */ + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + + case '*': /* multiply */ + next(1); /* skip over the operator */ + sym.val *= eval().val; + sym.type = DEFINED; + if( sym.val >= 01000000 ) + sym.val = ( sym.val + 1 ) & 0777777; + continue; + +#if 0 + case '%': /* divide !??? */ + /* + * neither '%' nor the divide symbol appear in FIO-DEC, + * does any known program use such an operator? + * Easily confused for "MOD", which is how C uses '%'! + */ + next(1); + sym.val /= eval().val; + sym.type = DEFINED; + continue; +#endif + + case '&': /* and */ + next(1); /* skip over the operator */ + sym.val &= eval().val; + sym.type = DEFINED; + continue; + + case '!': /* or */ + next(1); /* skip over the operator */ + sym.val |= eval().val; + sym.type = DEFINED; + continue; + + case '/': + case ')': + case ']': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym.val = 0; + break; + + default: + if (!ISEND(line[lexstart])) { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym.val = 0; + break; + } + } /* switch */ + break; /* break loop!! */ + } /* "forever" */ + return( sym ); +} /* getExpr */ + +/* + * return fio-dec code for next char + * embeds shifts as needed + */ +int +nextfiodec(int *ccase, int delim) +{ + unsigned char c; + + for (;;) { + if (cc >= maxcc) { + if (delim == -1) + return -1; + + /* XXX MUST NOT BE IN A REPEAT!! */ + readLine(); /* danger will robinson! */ + if (end_of_input) + return -1; + } + c = line[cc]; + switch (c) { + case '\n': + c = '\r'; + break; + case '\r': + continue; + } + break; + } + + if (delim != -1 && c == delim) { + if (*ccase == LC) { + cc++; /* eat delim */ + return -1; + } + *ccase = LC; + return CONCISE_LC; /* shift down first */ + } + + if (c > 0177) { /* non-ascii */ + errorMessage( &illegal_character, cc ); + c = 0; /* space?! */ + } + + c = ascii_to_fiodec[c&0177]; + if (c == BAD) { + errorMessage( &illegal_character, cc ); + c = 0; /* space?! */ + } + + if (!(c & *ccase)) { /* char not in current case? */ + *ccase ^= BC; /* switch case */ + if (*ccase == LC) + return CONCISE_LC; /* shift down */ + else + return CONCISE_UC; /* shift up */ + } + cc++; + return c & CHARBITS; +} + +/* + * Function: flex + * Synopsis: Handle data for "flexo" pseudo + * handle upper case by doing shifts + */ + +WORD32 flex() +{ + WORD32 w; + int shift; + int ccase; + + if (line[lexstart] == ' ') /* always? */ + next(0); + + /* original version appears to take next 3 characters, + * REGARDLESS of what they are (tab, newline, space?)! + */ + w = 0; + ccase = LC; /* current case */ + for (shift = 12; shift >= 0; shift -= 6) { + unsigned char c; + if( lexstart >= maxcc ) + break; + + c = line[lexstart]; + if (c == '\t' || c == '\n') { + if (ccase == LC) + break; + c = CONCISE_LC; /* shift down first */ + } + else { + if (c > 0177) { /* non-ascii */ + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + c = ascii_to_fiodec[c&0177]; + if (c == BAD) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + if (!(c & ccase)) { /* char not in current case? */ + ccase ^= BC; /* switch case */ + if (ccase == LC) + c = CONCISE_LC; /* shift down */ + else + c = CONCISE_UC; /* shift up */ + } + else + lexstart++; + } + w |= (c & CHARBITS) << shift; + } + /* error to get here w/ case == UC? nah. shift down could be next */ + return w; +} /* flex */ + +/* + * Function: getChar + * Synopsis: Handle data for "char" pseudo + */ + +WORD32 getChar() +{ + unsigned char c, pos; + + if( cc >= maxcc ) + return 0; /* XXX error? */ + pos = line[cc++]; + if (pos != 'l' && pos != 'm' && pos != 'r') { + errorMessage( &illegal_character, lexstart ); + return 0; + } + + if( cc >= maxcc ) + return 0; /* XXX error? */ + + c = line[cc++]; + if (c > 0177) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + c = ascii_to_fiodec[c]; + if (c == BAD) { + errorMessage( &illegal_character, lexstart ); + c = 0; + } + + if (!(c & LC)) { /* upper case only char? */ + c = CONCISE_UC; /* take a shift up */ + cc--; /* and leave char for next luser */ + } + + c &= CHARBITS; + switch (pos) { + case 'l': return c << 12; + case 'm': return c << 6; + case 'r': return c; + } + /* should not happen */ + return 0; +} /* flex */ + +/* Function: eval */ +/* Synopsis: Get the value of the current lexeme, and advance.*/ +SYM_T eval2() +{ + WORD32 digit; + WORD32 from; + SYM_T *sym; + WORD32 val; + SYM_T sym_eval; + + sym_eval.type = DEFINED; + sym_eval.name[0] = '\0'; + sym_eval.val = sym_eval.xref_index = sym_eval.xref_count = 0; + + val = 0; + + if( isLexSymbol()) { + sym = evalSymbol(); + if(!M_DEFINED( sym->type )) { + if( pass == 2 ) + errorSymbol( &undefined_symbol, sym->name, lexstart ); + next(1); + return( *sym ); + } + else if( M_PSEUDO(sym->type) || M_EPSEUDO(sym->type)) { + switch (sym->val) { + case DECIMAL: + radix = 10; + sym_eval.type = PSEUDO; + sym_eval.val = 0; /* has zero as a value! */ + break; + case OCTAL: + radix = 8; + sym_eval.type = PSEUDO; + sym_eval.val = 0; /* has zero as a value */ + break; + case FLEX: + next(1); /* skip keyword */ + sym_eval.val = flex(); + break; + case CHAR: + next(1); /* skip keyword */ + sym_eval.val = getChar(); + break; + default: + errorSymbol( &value_required, sym->name, lexstart ); + sym_eval.type = sym->type; + sym_eval.val = 0; + break; + } + next(1); + return( sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + next(1); + return( sym_eval ); + } + else + { + next(1); + return( *sym ); + } + } /* symbol */ + else if( isdigit( line[lexstart] )) { + from = lexstart; + val = 0; + while( from < lexterm ) { + if( isdigit( line[from] )) { + digit = line[from++] - '0'; + if( digit >= radix ) { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + break; + } + val = val * radix + digit; + } + else { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + break; + } + } + next(1); + sym_eval.val = val; + return( sym_eval ); + } /* digit */ + else { + switch( line[lexstart] ) { + case '.': /* Value of Current Location Counter */ + val = clc; + next(1); + break; + case '(': /* Generate literal */ + next(1); /* Skip paren */ + val = getExprs(); /* recurse */ + if( line[lexstart] == ')' ) + next(1); /* Skip end paren */ + sym_eval.val = literal(val); + return sym_eval; + case '[': /* parens!! */ + next(1); + sym_eval.val = getExprs(); /* mutual recursion */ + if( line[lexstart] == ']' ) + next(1); /* Skip close bracket */ + else + errorMessage( &illegal_character, lexstart ); + return sym_eval; + default: + switch( line[lexstart] ) { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + default: + errorMessage( &illegal_character, lexstart ); + break; + } /* error switch */ + val = 0; /* On error, set value to zero. */ + next(1); /* Go past illegal character. */ + } /* switch on first char */ + } /* not symbol or number */ + sym_eval.val = val; + return( sym_eval ); +} /* eval2 */ + + +SYM_T eval() { + SYM_T sym; + + switch (line[lexstart]) { + case '-': /* unary - */ + next(1); + sym = eval2(); /* skip op */ + sym.val ^= 0777777; + break; + case '+': /* unary + */ + next(1); /* skip op */ + /* fall */ + default: + sym = eval2(); + } + return sym; +} + +/* Function: incrementClc */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +WORD32 incrementClc() +{ + clc = (( clc + 1 ) & ADDRESS_FIELD ); + return( clc ); +} /* incrementClc */ + + +/* Function: readLine */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char inpline[LINELEN]; + + /* XXX panic if nrepeats > 0 (if self-feeding, do the backup here?) */ + + listLine(); /* List previous line if needed. */ + error_in_line = FALSE; /* No error in line. */ + + if(curmacro && *curmacro->mac_ptr == '\0') { /* end of macro? */ + struct macinv *mp; + + listed = TRUE; /* Already listed. */ + + /* Restore invoking line. */ + strcpy(line, curmacro->mac_line); + cc = lexstartprev = curmacro->mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + + mp = curmacro->prev; /* pop stack */ + free(curmacro); + curmacro = mp; + + return; + } /* end of macro */ + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( curmacro ) { /* Inside macro? */ + char mc; + + maxcc = 0; + do { + + mc = *curmacro->mac_ptr++; /* Next character. */ + /* watch for overflow? how could it?? */ + line[maxcc++] = mc; + } while( !ISEND( mc )); /* note: terminates on tab?! */ + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } /* inside macro */ + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ + READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) { /* More files? */ + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + list_title_set = FALSE; + goto READ_LINE; + } + else + end_of_input = TRUE; + } /* fgets failed */ + + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) { + if( inpline[ix] == '\f' ) { + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + } + else + line[iy++] = inpline[ix]; + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine */ + + +/* Function: listLine */ +/* Synopsis: Output a line to the listing file. */ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine */ + + +/* Function: printPageBreak */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + topOfForm( list_title, NULL ); + } +} /* printPageBreak */ + + +/* Function: printLine */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %6.6o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %6.6o\n", val ); + } + break; + + case LINE_LOC: + if( !listed ) + { + fprintf( listfile, "%5d %5.5o ", lineno, loc ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o\n", loc ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine */ + + +/* Function: printErrorMessages */ +/* Synopsis: Output any error messages from the current list of errors. */ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages */ + + +/* Function: punchObject */ +/* Synopsis: Put one character to object file */ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + fputc( val, objectfile ); +} /* punchObject */ + +/* Function: punchTriplet */ +/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ +void punchTriplet( WORD32 val ) +{ + punchObject((( val >> 12) & 077) | 0200 ); + punchObject((( val >> 6 ) & 077) | 0200 ); + punchObject(( val & 077) | 0200 ); +} /* punchTriplet */ + +void +eob() { + /* in case no "start" in file (an error?) */ +} + +/* Function: punchLeader */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0, objectfile ); + } + } +} /* punchLeader */ + +/* Function: punchOutObject */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, loc, val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject */ + + +/* Function: punchLocObjectRIM */ +/* Synopsis: Output the word in RIM mode */ +void punchLocObjectRIM( WORD32 loc, WORD32 val ) +{ + punchTriplet( DIO | loc ); + punchTriplet( val ); +} /* punchLocObject */ + +/* punch loader in RIM mode */ +void +punchLoader() { + int i; + + if (noinput) + return; + + for (i = 0; i < DIM(loader); i++) + punchLocObjectRIM(LOADERBASE+i, loader[i]); + punchTriplet( JMP | LOADERBASE ); +} + +/* + * flush out loader buffer; output a block: + * DIO start + * DIO end+1 + * .... data .... + * sum + */ +#define PW(X) { WORD32 x = X; sum += x; punchTriplet(x); } +void +flushLoader() { + WORD32 sum; + int i; + + if (loaderbufcount == 0) + return; + + sum = 0; + PW( DIO | loaderbufstart ); + PW( DIO | loaderbufstart + loaderbufcount ); + for (i = 0; i < loaderbufcount; i++) + PW( loaderbuf[i] ); + + /* roll over all the overflows at once */ + if (sum & ~0777777) + sum = (sum & 0777777) + (sum >> 18); + if (sum & 01000000) /* one more time */ + sum++; + PW( sum ); + + punchLeader(5); + loaderbufcount = 0; +} + +void punchLocObject( WORD32 loc, WORD32 val ) +{ + if (!rim_mode) { + if ((loc & LOADERBUFMASK) == 0 || /* full/force alignment */ + loaderbufcount > 0 && + loc != loaderbufstart + loaderbufcount) /* disjoint */ + flushLoader(); + if (loaderbufcount == 0) + loaderbufstart = loc; + loaderbuf[loaderbufcount++] = val; + } + else + punchLocObjectRIM( loc, val ); +} + +/* Function: literal */ +/* Synopsis: Add a value to the literal pool */ +WORD32 +literal( WORD32 value ) +{ + int i; + + if (nconst >= MAX_CONSTANTS) { + fprintf(stderr, "too many 'constants'; increase MAX_CONSTANTS\n"); + exit(1); + } + + if (pass == 1) { + if (++lit_count[nconst] == MAX_LITERALS) { + fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); + exit(1); + } + return lit_count[nconst]; + } + +#if 1 + /* + * pool constants; makes for a shorter tape + * (but "middle" constants blocks can't shrink) + */ + for (i = 0; i < nlit; i++) + if (litter[i] == value) + return lit_loc[nconst] + i; +#endif + + /* paranoia */ + if (nlit == MAX_LITERALS) { + fprintf(stderr, "too many literals; increase MAX_LITERALS\n"); + exit(1); + } + + /* not found, save it */ + litter[nlit] = value; + + /* use base for this block, determined on pass1 */ + return lit_loc[nconst] + nlit++; +} /* literal */ + + +/* Function: printSymbolTable */ +/* Synopsis: Output the symbol table. */ +/* XXX now prints FIXED symbols too */ +void printSymbolTable() +{ + int ix; + int symbol_lines; + SYM_T *sym; + char mark; + + symbol_lines = 0; + for (ix = 0, sym = symtab; ix < symbol_top; ix++, sym++) { + if (M_FIXED(sym->type) || M_PSEUDO(sym->type) || + M_MACRO(sym->type) || M_EPSEUDO(sym->type)) + continue; + + if (symbol_lines == 0) { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE; + } + + switch( sym->type & ( DEFINED | REDEFINED )) { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, "%c%-6.6s %6.6o\n", mark, sym->name, sym->val ); + symbol_lines--; + } +} /* printSymbolTable */ + + +/* Function: printPermanentSymbolTable */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " expunge\n/\n" ); + + for( ix = 0; ix < symbol_top; ix++ ) + { + int type = symtab[ix].type; + if( M_FIXED(type) && !M_PSEUDO(type) && !M_EPSEUDO(type) ) + fprintf( permfile, "\t%s=%o\n", + symtab[ix].name, symtab[ix].val ); + } + fclose( permfile ); +} /* printPermanentSymbolTable */ + + +/* Function: printCrossReference */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +void printCrossReference() +{ + int ix; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + SYM_T *sym; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + + for( ix = 0, sym = symtab; ix < symbol_top; ix++, sym++ ) { + if (M_FIXED(sym->type) && xreftab[sym->xref_index] == 0) + continue; + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + topOfForm( list_title, s_xref ); + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol */ + xc_refcount = sym->xref_count; + xc_index = sym->xref_index; + /* Determine how to label symbol on concordance. */ + /* XXX flag variables? */ + switch( sym->type & ( DEFINED | REDEFINED )) { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", sym->name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { + if( xc_cols >= XREF_COLUMNS ) { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + topOfForm( list_title, s_xref); + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } /* for */ +} /* printCrossReference */ + + +/* Function: topOfForm */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + if (!listfile) + return; + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + fprintf( listfile, "\f" ); + + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm */ + + +/* Function: lexemeToName */ +/* Synopsis: Convert the current lexeme into a string. */ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + int to; + + to = 0; + while( from < term && to < SYMLEN-1) { + char c = line[from++]; + if (ISOVERBAR(c)) + continue; + name[to++] = c; + } + name[to] = '\0'; + + return( name ); +} /* lexemeToName */ + +/* Function: defineLexeme */ +/* Synopsis: Put lexeme into symbol table with a value. */ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme */ + + +/* Function: defineSymbol */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name, type ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + /* moved inside "if pass2" -plb 10/2/03 allow redefinition + * of predefined symbols during pass1 + */ + return ( sym ); + } + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + if (sym->xref_index >= 0) { /* beware macro dummies */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + } + + /* Now set the value and the type. */ + sym->val = val & 0777777; + sym->type = type; + return( sym ); +} /* defineSymbol */ + + +/* Function: lookup */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +SYM_T *lookup( char *name, int type ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + SYM_T *best; /* best match */ + SYM_T *sym; + + /* YIKES! Search dummies (and "R") before anything else!! */ + if (curmacro && curmacro->defn) { + struct macdef *mdp = curmacro->defn; + int i; + + for (i = 0, sym = mdp->args; i <= mdp->nargs; i++, sym++) + if (strcmp(name, sym->name) == 0) + return sym; + } + + lx = 0; + rx = symbol_top - 1; + best = NULL; + while (lx <= rx) { + int mx = (lx + rx) / 2; /* Find center of search area. */ + int compare; + + sym = symtab + mx; + + compare = strcmp(name, sym->name); + if (compare < 0) + rx = mx - 1; + else if (compare > 0) + lx = mx + 1; + else { /* match */ + if (overbar && !M_DEFINED(sym->type) && pass == 2) { + sym->type = DEFINED; + sym->val = vars_addr++; + nvars++; + } + return sym; /* return exact match */ + } /* match */ + + /* save best non-exact match; MACRO returns last defined n-x match! */ + if ((M_PSEUDO(sym->type)||M_EPSEUDO(sym->type)||M_MACRO(sym->type)) && + strncmp(name, sym->name, 3) == 0) + best = sym; + } /* while */ + + /* return best match (pseudo or macro) if any for lookups (not defns) */ + if (best && type == UNDEFINED) + return best; + + /* Must put symbol in table if index is negative. */ + ix = lx; /* insertion point */ + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + symtab[rx + 1] = symtab[rx]; + + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + sym = symtab + ix; + strcpy( sym->name, name ); + sym->type = UNDEFINED; + sym->val = 0; + sym->xref_count = 0; + if( xref && pass == 2 && sym->xref_index >= 0) + xreftab[sym->xref_index] = 0; + + if (overbar) + nvars++; + + return sym; +} /* lookup */ + +/* Function: compareSymbols */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols */ + +/* Function: evalSymbol */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm ), UNDEFINED); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 && sym->xref_index >= 0) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol */ + + +/* Function: moveToEndOfLine */ +/* Synopsis: Move the parser input to the end of the current input line. */ +void moveToEndOfLine() +{ + while( !ISEND( line[cc] )) cc++; /* XXX wrong! will stop on a tab! */ + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine */ + +/* frame the next token in "line" with lexstart and lexterm indicies */ +void +next(int op) { + char c; + + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + c = line[cc]; + if (c == ' ') { + /* eat spaces */ + do { + c = line[++cc]; + } while (c == ' '); + if (op) /* looking for operators? */ + cc--; /* return one */ + } + + overbar = 0; + lexstart = cc; + c = line[cc]; + if( isalnum(c) || ISOVERBAR(c)) { + if (ISOVERBAR(c)) + overbar = 1; + do { + c = line[++cc]; + if (ISOVERBAR(c)) + overbar = 1; + } while (isalnum(c) || ISOVERBAR(c)); + } + else if(!ISDONE(c) || c == '\t') /* not end of line, or comment */ + cc++; /* advance past all punctuation */ + lexterm = cc; +} /* next */ + +BOOL isLexSymbol() +{ + int ix; + + /* XXX alpha within first 4? 3?? */ + for( ix = lexstart; ix < lexterm; ix++ ) + if(isalpha(line[ix])) + return TRUE; /* any position will do! */ + return FALSE; +} /* isLexSymbol */ + +/* + * from macro manual (F-36BP), p.18; + * + * "A macro-instruction definition consists of four parts; + * the pseudo-instruction _define_, the _macro instruction name_ + * amd _dummy symbol list,_ the _body_, and the pseudo-instruction + * _terminate_. Each part is followed by at least one tabulation or + * carriage return." + * + * and in the next paragraph; + * + * "The name is terminated by a _space_ or by a _tab_ or _cr_ + * if there is no dummy symbol list." + * + * This accepts tabs and/or a newline after define + * (but will accept a space), and only accepts spaces + * between macro and dummy names. + */ + +void +defineMacro() { + int lexstartsave; /* point to macro name */ + int index; /* point to error char */ + int error; /* error boolean */ + int i; + int count; + WORD32 length; + WORD32 value; + char termin[SYMLEN]; + char args[MAC_MAX_ARGS][SYMLEN]; /* macro & arg names */ + char body[MAC_MAX_LENGTH + 1]; + struct macdef *mdp; + SYM_T *sym; + + if (nrepeats) { + /* we can call readLine, so throw up hands now */ + errorLexeme( &define_in_repeat, lexstartprev ); + return; + } + + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + + /* not a tab or space */ + if (ISEND(line[lexstart])) { /* newline or EOS? */ + /* crock; next token should invisibly skip over line boundaries? */ + readLine(); + next(0); + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + } + + /* XXX pick up macro name out here */ + + count = 0; + index = 0; + error = FALSE; + lexstartsave = lexstart; + while (!ISDONE(line[lexstart]) && count < MAC_MAX_ARGS) { + if (!isalnum(line[lexstart]) && index == 0) + index = lexstart; /* error pointer */ + lexemeToName( args[count++], lexstart, lexterm ); + /* XXX error if NOT a comma (& not first dummy) ? */ + if (line[lexterm] == ',') + next(0); /* eat the comma */ + next(0); + if (line[lexstart] == ' ') + next(0); + } + if( count == 0 ) { /* No macro name. */ + errorMessage( &no_macro_name, lexstartsave ); + error = TRUE; + } + else if( index ) { /* Bad argument name. */ + errorMessage( &bad_dummy_arg, index ); + error = TRUE; + } + else if( mac_count >= MAC_TABLE_LENGTH ) { + errorMessage( ¯o_table_full, lexstartsave ); + error = TRUE; + } + else { + value = mac_count++; /* sym value is index into mac */ + defineSymbol( args[0], value, MACRO, lexstartsave ); + } + + for( length = 0;; ) { + readLine(); + if (end_of_input) + break; + next(0); + while (line[lexstart] == ' ' || line[lexstart] == '\t') + next(0); + + lexemeToName( termin, lexstart, lexterm ); /* just look at line? */ + if (strncmp( termin, "term", 4 ) == 0) + break; + + if (!error) { + int ll = strlen(line); + int allblank = FALSE; + + /* don't save blank lines! */ + for( i = 0; i < ll && allblank; i++ ) + if(!ISBLANK(line[i])) + allblank = FALSE; + + if (allblank) /* nothing but air? */ + continue; /* skip it! */ + + if ((length + ll + 1) >= MAC_MAX_LENGTH ) { + errorMessage (¯o_too_long, lexstart ); + error = TRUE; + continue; + } + + strcpy(body+length, line); + length += ll; + } + } /* for */ + if( error ) + return; + + mdp = calloc(1, sizeof(struct macdef) + length); + if (mdp == NULL) { + fprintf(stderr, "error allocating memory for macro definition\n"); + exit(1); + } + mac_defs[value] = mdp; + + strncpy(mdp->body, body, length); + mdp->body[length] = '\0'; + mdp->nargs = count - 1; + + /* + * save dummy names + * symbol slot 0 reserved for "r" symbol + * move SYM_T entries to macinv to allow recursion + */ + sym = mdp->args; + sym->type = DEFINED; + strcpy(sym->name, "R"); + sym->val = 0; + sym->xref_index = -1; /* ??? allow xref? */ + sym++; + + for (i = 1; i <= mdp->nargs; i++, sym++) { + sym->type = DEFINED; + strcpy(sym->name, args[i]); + sym->val = 0; + sym->xref_index = -1; /* don't xref!! */ + } +} /* defineMacro */ + +/* VARIABLES pseudo-op */ +void +variables() { + /* XXX error if "variables" already seen (in this pass) */ + /* XXX error if different address on pass 2 */ + if (pass == 2) + printLine( line, clc, 0, LINE_LOC ); + vars_addr = clc; + vars_end = clc = (clc + nvars) & ADDRESS_FIELD; + if (pass == 2) + printLine( line, clc, 0, LINE_LOC); +} + +/* TEXT pseudo-op */ +void +text(void) +{ + char delim; + WORD32 w; + int count; + int ccase; + /* XXX error in repeat!! */ + do { + if (cc == maxcc) { + /* XXX EOL before delim found!!! */ + fprintf(stderr, "FIX ME!\n"); + return; + } + delim = line[cc++]; + } while (delim == ' '); /* others? NL */ + + w = count = 0; + ccase = LC; + for (;;) { + int c = nextfiodec(&ccase, delim); + if (c == -1) + break; + w |= c << ((2-count)*6); + if (++count == 3) { + punchOutObject(clc, w); /* punch it! */ + incrementClc(); + count = w = 0; + } + } + if (count > 0) { + punchOutObject(clc, w); /* punch remainder */ + incrementClc(); + } +} + +/* CONSTANTS pseudo-op */ +void +constants(void) { + int i; + + /* XXX illegal inside macro (curmacro != NULL) */ + + if (pass == 1) { + lit_loc[nconst] = clc; + + /* just use addition?! */ + for (i = 0; i < lit_count[nconst]; i++) + incrementClc(); + + nconst++; + return; + } + + /* pass 2: */ + /* XXX complain if clc != lit_base[nconst]? */ + + for (i = 0; i < lit_count[nconst]; i++) { + if (i < nlit) + punchOutObject( clc, litter[i] & 0777777); /* punch it! */ + incrementClc(); + } + + nconst++; + nlit = 0; /* litter[] now empty */ +} /* constants */ + + +/* process pseudo-ops + * return FALSE if line scan should end (no longer used) + */ +BOOL pseudo( PSEUDO_T val ) +{ + int count; + int repeatstart; + + switch( (PSEUDO_T) val ) { + case CONSTANTS: + next(0); /* Skip symbol */ + constants(); + break; + + case VARIABLES: + next(0); /* Skip symbol */ + variables(); + break; + + case DEFINE: + next(0); /* Skip symbol */ + defineMacro(); + return FALSE; + break; + + case REPEAT: + next(0); /* Skip symbol */ + + /* NOTE!! constant followed by SPACE picked up as expression!! */ + count = getExprs() & ADDRESS_FIELD; + /* XXX error if sign bit set? */ + + /* allow comma, but do not require */ + if( line[lexstart] == ',') + next(0); + + nrepeats++; + repeatstart = lexstart; /* save line start */ + while (count-- > 0) { + cc = repeatstart; /* reset input pointer */ + processLine(); /* recurse! */ + } + cc = maxcc; + nrepeats--; + + return FALSE; + break; + + case START: + next(0); /* Skip symbol */ + /* XXX illegal in macro or repeat */ + flushLoader(); + if (!ISDONE(line[lexstart])) { + if (line[lexstart] == ' ') + next(0); + start_addr = getExprs() & ADDRESS_FIELD; + next(0); + printLine( line, 0, start_addr, LINE_VAL ); + /* MACRO punches 4" of leader */ + punchTriplet(JMP | start_addr); + /* MACRO punches 24" of leader? */ + } + /* + * handle multiple tapes concatenated into one file!! + * have command line option?? treat "start" as EOF?? + */ + list_title_set = FALSE; + return FALSE; + + case TEXT: + /* NOTE!! no next()! */ + text(); + break; + + case NOINPUT: + next(0); /* Skip symbol */ + noinput = TRUE; + break; + + case EXPUNGE: + next(0); /* Skip symbol */ + if (pass == 1) + init_symtab(); + break; + + default: + break; + } /* end switch for pseudo-ops */ + return TRUE; /* keep scanning */ +} /* pseudo */ + + +/* Function: errorLexeme */ +/* Synopsis: Display an error message using the current lexical element. */ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme */ + + +/* Function: errorSymbol */ +/* Synopsis: Display an error message with a given string. */ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, ":%d:%d", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol */ + + +/* Function: errorMessage */ +/* Synopsis: Display an error message without a name argument. */ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, ":%d:%d", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage */ + +/* Function: saveError */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + printErrorMessages(); +} /* saveError */ + +/* create a "symbol punch" for DDT */ +/* MUST be called after object file closed; we reuse the FILE*! */ + +void +dump_symbols(void) { + int ix; + WORD32 addr; + + objectfile = fopen( sympathname, "wb" ); + if (!objectfile) { + perror(sympathname); + return; + } + + punchLeader(0); + punchLoader(); + punchLeader(5); + + /* XXX fudge addr -- get count, and subtract 2N from 07750? */ + addr = 05000; + + for( ix = 0; ix < symbol_top; ix++ ) { + int i, type; + WORD32 name; + + type = symtab[ix].type; + if (M_FIXED(type) || M_PSEUDO(type) || M_MACRO(type)) + continue; + + name = 0; + for (i = 0; i < 3; i++) { + char c; + + c = symtab[ix].name[i]; + /* XXX leave on NUL? */ + + c = ascii_to_fiodec[tolower(c) & 0177]; + /* XXX check for BAD entries? */ + + /* XXX OR in val<<(3-i)*6?? */ + name <<= 6; + name |= c & CHARBITS; + } + punchLocObject(addr++, permute(name)); + punchLocObject(addr++, symtab[ix].val); + } + flushLoader(); + punchTriplet( JMP ); /* ??? */ + punchLeader(0); + fclose(objectfile); +} diff --git a/crossassemblers/macro11/.indent.pro b/crossassemblers/macro11/.indent.pro index ebdb836..53b36a8 100755 --- a/crossassemblers/macro11/.indent.pro +++ b/crossassemblers/macro11/.indent.pro @@ -1,45 +1,45 @@ ---blank-lines-after-commas ---blank-lines-after-declarations ---blank-lines-after-procedures ---braces-on-if-line ---braces-on-struct-decl-line ---break-before-boolean-operator ---break-function-decl-args ---break-function-decl-args-end ---case-indentation0 ---comment-indentation40 ---continuation-indentation8 ---continue-at-parentheses ---cuddle-do-while ---cuddle-else ---declaration-indentation16 ---dont-break-procedure-type ---dont-format-comments ---indent-level4 ---honour-newlines ---leave-optional-blank-lines ---line-comments-indentation0 ---line-length112 ---no-blank-lines-before-block-comments ---no-space-after-function-call-names ---no-tabs ---paren-indentation4 ---space-special-semicolon ---tab-size8 --T ADDR_MODE --T ARG --T BUFFER --T EX_TREE --T FILE --T MACRO --T MACRO_STREAM --T MLB --T MLBENT --T STACK --T STREAM --T SECTION --T SYMBOL --T SYMBOL_ITER --T SYMBOL_TABLE --T TEXT_COMPLEX --T TEXT_RLD +--blank-lines-after-commas +--blank-lines-after-declarations +--blank-lines-after-procedures +--braces-on-if-line +--braces-on-struct-decl-line +--break-before-boolean-operator +--break-function-decl-args +--break-function-decl-args-end +--case-indentation0 +--comment-indentation40 +--continuation-indentation8 +--continue-at-parentheses +--cuddle-do-while +--cuddle-else +--declaration-indentation16 +--dont-break-procedure-type +--dont-format-comments +--indent-level4 +--honour-newlines +--leave-optional-blank-lines +--line-comments-indentation0 +--line-length112 +--no-blank-lines-before-block-comments +--no-space-after-function-call-names +--no-tabs +--paren-indentation4 +--space-special-semicolon +--tab-size8 +-T ADDR_MODE +-T ARG +-T BUFFER +-T EX_TREE +-T FILE +-T MACRO +-T MACRO_STREAM +-T MLB +-T MLBENT +-T STACK +-T STREAM +-T SECTION +-T SYMBOL +-T SYMBOL_ITER +-T SYMBOL_TABLE +-T TEXT_COMPLEX +-T TEXT_RLD diff --git a/crossassemblers/macro11/obj2bin/.gitignore b/crossassemblers/macro11/obj2bin/.gitignore index a09a578..e026898 100644 --- a/crossassemblers/macro11/obj2bin/.gitignore +++ b/crossassemblers/macro11/obj2bin/.gitignore @@ -1,4 +1,4 @@ -# ignore these files: -# ignore these directories: -tests/ -# the end +# ignore these files: +# ignore these directories: +tests/ +# the end diff --git a/crossassemblers/macro7/macro7.c b/crossassemblers/macro7/macro7.c index e0b38af..f536433 100644 --- a/crossassemblers/macro7/macro7.c +++ b/crossassemblers/macro7/macro7.c @@ -1,3083 +1,3083 @@ -/******************************************************************************/ -/* */ -/* Program: MACRO7 */ -/* File: macro7.c */ -/* Author: Gary A. Messenbrink */ -/* MACRO7 modifications: Bob Supnik (:) : error: at Loc = */ -/* */ -/* An example error message is: */ -/* */ -/* bintst.7(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ -/* */ -/* The error diagnostics put in the listing start with a two character */ -/* error code (if appropriate) and a short message. A carat '^' is */ -/* placed under the item in error if appropriate. */ -/* An example error message is: */ -/* */ -/* 17 07616 3000 DAC UNDEF */ -/* UD undefined ^ */ -/* 18 07617 1777 TAD I DUMMY */ -/* */ -/* When an indirect is generated, an at character '@' is placed after the */ -/* the instruction value in the listing as an indicator as follows: */ -/* */ -/* 14 03716 1777@ TAD OFFPAG */ -/* */ -/* Undefined symbols are marked in the symbol table listing by prepending */ -/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ -/* listing by prepending a '#' to the symbol. Examples are: */ -/* */ -/* #REDEF 04567 */ -/* SWITCH 07612 */ -/* ?UNDEF 00000 */ -/* */ -/* Refer to the code for the diagnostic messages generated. */ -/* */ -/* REFERENCES: */ -/* This assembler is based on the pal assember by: */ -/* Douglas Jones and */ -/* Rich Coon */ -/* */ -/* COPYRIGHT NOTICE: */ -/* This is free software. There is no fee for using it. You may make */ -/* any changes that you wish and also give it away. If you can make */ -/* a commercial product out of it, fine, but do not put any limits on */ -/* the purchaser's right to do the same. If you improve it or fix any */ -/* bugs, it would be nice if you told me and offered me a copy of the */ -/* new version. */ -/* */ -/* */ -/* Amendments Record: */ -/* Version Date by Comments */ -/* ------- ------- --- --------------------------------------------------- */ -/* v1.0 12Apr96 GAM Original */ -/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ -/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ -/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ -/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ -/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ -/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ -/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ -/* v3.0 14Feb97 RMS MACRO8X features. */ -/* */ -/******************************************************************************/ - -#include -#include -#include -#include - -#define LINELEN 96 -#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ -#define NAMELEN 128 -#define SYMBOL_COLUMNS 5 -#define SYMLEN 7 -#define SYMBOL_TABLE_SIZE 8192 -#define MAC_MAX_ARGS 20 /* Must be < 26 */ -#define MAC_MAX_LENGTH 8192 -#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ -#define TITLELEN 63 -#define XREF_COLUMNS 8 - -#define ADDRESS_FIELD 0017777 -#define LIT_BASE 0017400 -#define INDIRECT_BIT 0020000 -#define LAST_PAGE_LOC 0017777 -#define OP_CODE 0740000 - -/* Macro to get the number of elements in an array. */ -#define DIM(a) (sizeof(a)/sizeof(a[0])) - -/* Macro to get the address plus one of the end of an array. */ -#define BEYOND(a) ((a) + DIM(A)) - -#define is_blank(c) ((c==' ') || (c=='\f') || (c=='>')) -#define isend(c) ((c=='\0')|| (c=='\n')) -#define isdone(c) ((c=='/') || (isend(c)) || (c=='\t')) - -/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ -/* (true) if the stated condtion is met. */ -/* Use these to test attributes. The proper bits are extracted and then */ -/* tested. */ -#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) -#define M_DEFINED(s) ((s & DEFINED) == DEFINED) -#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) -#define M_FIXED(s) ((s & FIXED) == FIXED) -#define M_LABEL(s) ((s & LABEL) == LABEL) -#define M_MRI(s) ((s & MRI) == MRI) -#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) -#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) -#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) -#define M_MACRO(s) ((s & MACRO) == MACRO) -#define M_UNDEFINED(s) (!M_DEFINED(s)) -#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) - -/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ -#define M_DEF(s) (M_DEFINED(s)) -#define M_COND(s) (M_DEFINED(s)) -#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) - -typedef unsigned char BOOL; -typedef unsigned char BYTE; -typedef int WORD32; - -#ifndef FALSE - #define FALSE 0 - #define TRUE (!FALSE) -#endif - -/* Line listing styles. Used to control listing of lines. */ -enum linestyle_t -{ - LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL -}; -typedef enum linestyle_t LINESTYLE_T; - -/* Symbol Types. */ -/* Note that the names that have FIX as the suffix contain the FIXED bit */ -/* included in the value. */ -/* */ -/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ -/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ -/* defined or undefined. The condition bit is set when the symbol is defined */ -/* during pass 1 and reset on pass 2 at the location the symbol was defined */ -/* during pass 1. When processing conditionals during pass 2, if the symbol */ -/* is defined and the condition bit is set, the symbol is treated as if it */ -/* were undefined. This gives consistent behavior of the conditional */ -/* pseudo-ops during both pass 1 and pass 2. */ -enum symtyp -{ - UNDEFINED = 0000, - DEFINED = 0001, - FIXED = 0002, - MRI = 0004 | DEFINED, - LABEL = 0010 | DEFINED, - REDEFINED = 0020 | DEFINED, - DUPLICATE = 0040 | DEFINED, - PSEUDO = 0100 | FIXED | DEFINED, - CONDITION = 0200 | DEFINED, - MACRO = 0400 | DEFINED, - MRIFIX = MRI | FIXED | DEFINED, - DEFFIX = DEFINED | FIXED, - NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED -}; -typedef enum symtyp SYMTYP; - -enum pseudo_t -{ - DECIMAL, DEFINE, EJECT, IFDEF, IFNDEF, IFNZERO, IFZERO, - LIST, NOLIST, OCTAL, START, TEXT, TITLE, VFD -}; -typedef enum pseudo_t PSEUDO_T; - -struct sym_t -{ - SYMTYP type; - char name[SYMLEN]; - WORD32 val; - WORD32 xref_index; - WORD32 xref_count; -}; -typedef struct sym_t SYM_T; - -struct lpool_t -{ - WORD32 error; /* True if error message has been printed. */ - WORD32 pool[LIT_BASE]; -}; -typedef struct lpool_t LPOOL_T; - -struct emsg_t -{ - char *list; - char *file; -}; -typedef struct emsg_t EMSG_T; - -struct errsave_t -{ - char *mesg; - WORD32 col; -}; -typedef struct errsave_t ERRSAVE_T; - -/*----------------------------------------------------------------------------*/ - -/* Function Prototypes */ - -int binarySearch( char *name, int start, int symbol_count ); -int copyMacLine( int length, int from, int term, int nargs ); -int compareSymbols( const void *a, const void *b ); -void conditionFalse( void ); -void conditionTrue( void ); -SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); -void endOfBinary( void ); -void errorLexeme( EMSG_T *mesg, WORD32 col ); -void errorMessage( EMSG_T *mesg, WORD32 col ); -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); -SYM_T *eval( void ); -SYM_T *evalSymbol( void ); -void getArgs( int argc, char *argv[] ); -SYM_T *getExpr( void ); -WORD32 getExprs( void ); -WORD32 incrementClc( void ); -WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); -char *lexemeToName( char *name, WORD32 from, WORD32 term ); -void listLine( void ); -SYM_T *lookup( char *name ); -void moveToEndOfLine( void ); -void nextLexBlank( void ); -void nextLexeme( void ); -void onePass( void ); -void printCrossReference( void ); -void printErrorMessages( void ); -void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); -void printPageBreak( void ); -void printPermanentSymbolTable( void ); -void printSymbolTable( void ); -BOOL pseudoOperators( PSEUDO_T val ); -void punchLocObject( WORD32 loc, WORD32 val ); -void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); -void punchOutObject( WORD32 loc, WORD32 val ); -void punchLeader( WORD32 count ); -void punchObject( WORD32 val ); -void punchTriplet( WORD32 val ); -void readLine( void ); -void saveError( char *mesg, WORD32 cc ); -BOOL testForLiteralCollision( WORD32 loc ); -void topOfForm( char *title, char *sub_title ); - -/*----------------------------------------------------------------------------*/ - -/* Table of pseudo-ops (directives) which are used to setup the symbol */ -/* table on startup and when the EXPUNGE pseudo-op is executed. */ -SYM_T pseudo[] = -{ - { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ - { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ - { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ - { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ - { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ - { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ - { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ - { PSEUDO, "LIST", LIST }, /* Enable listing. */ - { PSEUDO, "NOLIST", NOLIST }, /* Disable listing. */ - { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ - { PSEUDO, "START", START }, /* Set starting address. */ - { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ - { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ - { PSEUDO, "VFD", VFD }, /* Variable field definition. */ -}; - -/* Symbol Table */ -/* The table is put in lexical order on startup, so symbols can be */ -/* inserted as desired into the initial table. */ -#define DAC 0040000 -#define JMP 0600000 -SYM_T permanent_symbols[] = -{ - /* Memory Reference Instructions */ - { MRIFIX, "CAL", 0000000 }, - { MRIFIX, "DAC", 0040000 }, - { MRIFIX, "JMS", 0100000 }, - { MRIFIX, "DZM", 0140000 }, - { MRIFIX, "LAC", 0200000 }, - { MRIFIX, "XOR", 0240000 }, - { MRIFIX, "ADD", 0300000 }, - { MRIFIX, "TAD", 0340000 }, - { MRIFIX, "XCT", 0400000 }, - { MRIFIX, "ISZ", 0440000 }, - { MRIFIX, "AND", 0500000 }, - { MRIFIX, "SAD", 0540000 }, - { MRIFIX, "JMP", 0600000 }, - { MRIFIX, "I", 0020000 }, - { DEFFIX, "EAE", 0640000 }, - { DEFFIX, "IOT", 0700000 }, - { DEFFIX, "OPR", 0740000 }, - { DEFFIX, "LAW", 0760000 }, - { DEFFIX, "LAM", 0777777 }, - /* EAE Microinstructions */ - { DEFFIX, "OSC", 0640001 }, - { DEFFIX, "LACS", 0641001 }, - { DEFFIX, "OMQ", 0640002 }, - { DEFFIX, "LACQ", 0641002 }, - { DEFFIX, "CMQ", 0640004 }, - { DEFFIX, "CLQ", 0650000 }, - { DEFFIX, "LMQ", 0652000 }, - { DEFFIX, "ABS", 0644000 }, - { DEFFIX, "GSM", 0664000 }, - { DEFFIX, "MUL", 0653122 }, - { DEFFIX, "MULS", 0657122 }, - { DEFFIX, "DIV", 0640323 }, - { DEFFIX, "DIVS", 0644323 }, - { DEFFIX, "IDIV", 0653323 }, - { DEFFIX, "IDIVS", 0657323 }, - { DEFFIX, "FRDIV", 0650323 }, - { DEFFIX, "FRDIVS", 0654323 }, - { DEFFIX, "NORM", 0640444 }, - { DEFFIX, "NORMS", 0660444 }, - { DEFFIX, "LRS", 0640500 }, - { DEFFIX, "LRSS", 0660500 }, - { DEFFIX, "LLS", 0640600 }, - { DEFFIX, "LLSS", 0660600 }, - { DEFFIX, "ALS", 0640700 }, - { DEFFIX, "ALSS", 0660700 }, - /* Operate Microinstructions */ - { DEFFIX, "NOP", 0740000 }, - { DEFFIX, "CMA", 0740001 }, - { DEFFIX, "CML", 0740002 }, - { DEFFIX, "OAS", 0740004 }, - { DEFFIX, "LAS", 0750004 }, - { DEFFIX, "RAL", 0740010 }, - { DEFFIX, "RCL", 0744010 }, - { DEFFIX, "RTL", 0742010 }, - { DEFFIX, "RAR", 0740020 }, - { DEFFIX, "RCR", 0744020 }, - { DEFFIX, "RTR", 0742020 }, - { DEFFIX, "HLT", 0740040 }, - { DEFFIX, "XX", 0740040 }, - { DEFFIX, "SMA", 0740100 }, - { DEFFIX, "SZA", 0740200 }, - { DEFFIX, "SNL", 0740400 }, - { DEFFIX, "SKP", 0741000 }, - { DEFFIX, "SPA", 0741100 }, - { DEFFIX, "SNA", 0741200 }, - { DEFFIX, "SZL", 0741400 }, - { DEFFIX, "CLL", 0744000 }, - { DEFFIX, "STL", 0744002 }, - { DEFFIX, "CLA", 0750000 }, - { DEFFIX, "CLC", 0750001 }, - { DEFFIX, "GLK", 0750010 }, - /* CPU IOT's */ - { DEFFIX, "CLSF", 0700001 }, - { DEFFIX, "IOF", 0700002 }, - { DEFFIX, "ION", 0700042 }, - { DEFFIX, "ITON", 0700062 }, - { DEFFIX, "CLOF", 0700004 }, - { DEFFIX, "CLON", 0700044 }, - { DEFFIX, "TTS", 0703301 }, - { DEFFIX, "SKP7", 0703341 }, - { DEFFIX, "CAF", 0703302 }, - { DEFFIX, "SEM", 0707701 }, - { DEFFIX, "EEM", 0707702 }, - { DEFFIX, "EMIR", 0707742 }, - { DEFFIX, "LEM", 0707704 }, - /* High Speed Paper Tape Reader */ - { DEFFIX, "RSF", 0700101 }, - { DEFFIX, "RRB", 0700112 }, - { DEFFIX, "RCF", 0700102 }, - { DEFFIX, "RSA", 0700104 }, - { DEFFIX, "RSB", 0700144 }, - /* High Speed Paper Tape Punch */ - { DEFFIX, "PSF", 0700201 }, - { DEFFIX, "PCF", 0700202 }, - { DEFFIX, "PSA", 0700204 }, - { DEFFIX, "PLS", 0700204 }, - { DEFFIX, "PSB", 0700244 }, - /* Keyboard */ - { DEFFIX, "KSF", 0700301 }, - { DEFFIX, "KRB", 0700312 }, - { DEFFIX, "IORS", 0700314 }, - /* Teleprinter */ - { DEFFIX, "TSF", 0700401 }, - { DEFFIX, "TCF", 0700402 }, - { DEFFIX, "TLS", 0700406 }, - /* Line Printer */ - { DEFINED, "LPSF", 0706501 }, - { DEFINED, "LPCB", 0706502 }, - { DEFINED, "LPB1", 0706566 }, - { DEFINED, "LPB2", 0706526 }, - { DEFINED, "LPB3", 0706546 }, - { DEFINED, "LPSE", 0706601 }, - { DEFINED, "LPCF", 0706602 }, - { DEFINED, "LPPB", 0706606 }, - { DEFINED, "LPLS", 0706626 }, - { DEFINED, "LPPS", 0706646 }, - /* Card Reader */ - { DEFFIX, "CRSF", 0706701 }, - { DEFFIX, "CRRB", 0706712 }, - { DEFFIX, "CRSA", 0706704 }, - { DEFFIX, "CRSB", 0706744 }, - /* DECtape */ - { DEFFIX, "MMDF", 0707501 }, - { DEFFIX, "MMEF", 0707541 }, - { DEFFIX, "MMRD", 0707512 }, - { DEFFIX, "MMWR", 0707504 }, - { DEFFIX, "MMBF", 0707601 }, - { DEFFIX, "MMRS", 0707612 }, - { DEFFIX, "MMLC", 0707604 }, - { DEFFIX, "MMSE", 0707644 }, -}; /* End-of-Symbols for Permanent Symbol Table */ - -/* Global variables */ -SYM_T *symtab; /* Symbol Table */ -int symbol_top; /* Number of entries in symbol table. */ - -SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ -int number_of_fixed_symbols; - -/*----------------------------------------------------------------------------*/ - -WORD32 *xreftab; /* Start of the concordance table. */ - -ERRSAVE_T error_list[20]; -int save_error_count; - -LPOOL_T cp; /* Storage for current page constants. */ - -char s_detected[] = "detected"; -char s_error[] = "error"; -char s_errors[] = "errors"; -char s_no[] = "No"; -char s_page[] = "Page"; -char s_symtable[] = "Symbol Table"; -char s_xref[] = "Cross Reference"; - -/* Assembler diagnostic messages. */ -/* Some attempt has been made to keep continuity with the PAL-III and */ -/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ -/* exists, then the indicator is put in the listing as the first two */ -/* characters of the diagnostic message. The PAL-III indicators where used */ -/* when there was a choice between using MACRO-8 and PAL-III indicators. */ -/* The character pairs and their meanings are: */ -/* DT Duplicate Tag (symbol) */ -/* IC Illegal Character */ -/* ID Illegal Redefinition of a symbol. An attempt was made to give */ -/* a symbol a new value not via =. */ -/* IE Illegal Equals An equal sign was used in the wrong context, */ -/* (e.g., A+B=C, or TAD A+=B) */ -/* II Illegal Indirect An off page reference was made, but a literal */ -/* could not be generated because the indirect bit was already set. */ -/* IR Illegal Reference (address is not on current page or page zero) */ -/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ -/* RD ReDefintion of a symbol */ -/* ST Symbol Table full */ -/* UA Undefined Address (undefined symbol) */ -/* ZE Zero Page Exceeded (see above, or out of space) */ -EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; -EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; -EMSG_T illegal_character = { "IC illegal char", "illegal character" }; -EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; -EMSG_T label_syntax = { "IC label syntax", "label syntax" }; -EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; -EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; -EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; -EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; -EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; -EMSG_T illegal_reference = { "IR off page", "illegal reference" }; -EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; -EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; -EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; -EMSG_T literal_gen_off = { "lit generation off", - "literal generation disabled" }; -EMSG_T literal_overflow = { "PE page exceeded", - "current page literal capacity exceeded" }; -EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; -EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; -EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; -EMSG_T illegal_vfd_value = { "width out of range", - "VFD field width not in range" }; -EMSG_T no_literal_value = { "no value", "No literal value" }; -EMSG_T text_string = { "no delimiter", - "Text string delimiters not matched" }; -EMSG_T lt_expected = { "'<' expected", "'<' expected" }; -EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; -EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; -EMSG_T bad_dummy_arg = { "bad dummy arg", - "Bad dummy argument following DEFINE" }; -EMSG_T macro_too_long = { "macro too long", "Macro too long" }; -EMSG_T no_virtual_memory = { "out of memory", - "Insufficient memory for macro" }; -EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; - -/*----------------------------------------------------------------------------*/ - -FILE *errorfile; -FILE *infile; -FILE *listfile; -FILE *listsave; -FILE *objectfile; -FILE *objectsave; - -char errorpathname[NAMELEN]; -char filename[NAMELEN]; -char listpathname[NAMELEN]; -char objectpathname[NAMELEN]; -char *pathname; -char permpathname[NAMELEN]; - -char mac_buffer[MAC_MAX_LENGTH + 1]; -char *mac_bodies[MAC_TABLE_LENGTH]; -char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; -int mac_arg_pos[26] = { 0 }; - -int list_lineno; -int list_pageno; -char list_title[4*LINELEN]; -BOOL list_title_set; /* Set if TITLE pseudo-op used. */ -char line[4*LINELEN]; /* Input line. */ -int lineno; /* Current line number. */ -char mac_line[4*LINELEN]; /* Saved macro invocation line. */ -int page_lineno; /* print line number on current page. */ -WORD32 listed; /* Listed flag. */ -WORD32 listedsave; - -WORD32 cc; /* Column Counter (char position in line). */ -WORD32 checksum; /* Generated checksum */ -BOOL binary_data_output; /* Set true when data has been output. */ -WORD32 clc; /* Location counter */ -char delimiter; /* Character immediately after eval'd term. */ -BOOL end_of_input; /* End of all input files. */ -int errors; /* Number of errors found so far. */ -BOOL error_in_line; /* TRUE if error on current line. */ -int errors_pass_1; /* Number of errors on pass 1. */ -int filix_curr; /* Index in argv to current input file. */ -int filix_start; /* Start of input files in argv. */ -BOOL indirect_generated; /* TRUE if an off page address generated. */ -WORD32 lexstartprev; /* Where previous lexeme started. */ -WORD32 lextermprev; /* Where previous lexeme ended. */ -WORD32 lexstart; /* Index of current lexeme on line. */ -WORD32 lexterm; /* Index of character after current lexeme. */ -WORD32 lit_loc; /* Base of literal pool. */ -WORD32 mac_cc; /* Saved cc after macro invocation. */ -WORD32 mac_count; /* Total macros defined. */ -char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ -WORD32 maxcc; /* Current line length. */ -BOOL nomac_exp; /* No macro expansions. */ -WORD32 pass; /* Number of current pass. */ -BOOL print_permanent_symbols; -WORD32 radix; /* Default number radix. */ -BOOL rim_mode; /* RIM mode output. */ -int save_argc; /* Saved argc. */ -char **save_argv; /* Saved *argv[]. */ -WORD32 start_addr; /* Saved start address. */ -BOOL symtab_print; /* Print symbol table flag */ -BOOL xref; - -SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ -SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ -SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ - - -/******************************************************************************/ -/* */ -/* Function: main */ -/* */ -/* Synopsis: Starting point. Controls order of assembly. */ -/* */ -/******************************************************************************/ -int main( int argc, char *argv[] ) -{ - int ix; - int space; - - save_argc = argc; - save_argv = argv; - - /* Set the default values for global symbols. */ - binary_data_output = FALSE; - print_permanent_symbols = FALSE; - nomac_exp = TRUE; - rim_mode = TRUE; - symtab_print = FALSE; - xref = FALSE; - pathname = NULL; - for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) - { - mac_bodies[ix] = NULL; - } - - /* Get the options and pathnames */ - getArgs( argc, argv ); - - /* Setup the error file in case symbol table overflows while installing the */ - /* permanent symbols. */ - errorfile = fopen( errorpathname, "w" ); - errors = 0; - save_error_count = 0; - pass = 0; /* This is required for symbol table initialization. */ - symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); - - if( symtab == NULL ) - { - fprintf( stderr, "Could not allocate memory for symbol table.\n"); - exit( -1 ); - } - - /* Place end marker in symbol table. */ - symtab[0] = sym_undefined; - symbol_top = 0; - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Enter the pseudo-ops into the symbol table */ - for( ix = 0; ix < DIM( pseudo ); ix++ ) - { - defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); - } - - /* Enter the predefined symbols into the table. */ - /* Also make them part of the permanent symbol table. */ - for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) - { - defineSymbol( permanent_symbols[ix].name, - permanent_symbols[ix].val, - permanent_symbols[ix].type, 0 ); - } - - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Do pass one of the assembly */ - checksum = 0; - pass = 1; - onePass(); - errors_pass_1 = errors; - - /* Set up for pass two */ - errorfile = fopen( errorpathname, "w" ); - objectfile = fopen( objectpathname, "wb" ); - objectsave = objectfile; - - listfile = fopen( listpathname, "w" ); - listsave = listfile; - - punchLeader( 0 ); - checksum = 0; - - /* Do pass two of the assembly */ - errors = 0; - save_error_count = 0; - - if( xref ) - { - /* Get the amount of space that will be required for the concordance. */ - for( space = 0, ix = 0; ix < symbol_top; ix++ ) - { - symtab[ix].xref_index = space; /* Index into concordance table. */ - space += symtab[ix].xref_count + 1; - symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ - - } - /* Allocate the necessary space. */ - xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); - - /* Clear the cross reference space. */ - for( ix = 0; ix < space; ix++ ) - { - xreftab[ix] = 0; - } - } - pass = 2; - onePass(); - - /* Undo effects of NOPUNCH for any following checksum */ - objectfile = objectsave; - - /* Works great for trailer. */ - punchLeader( 1 ); - - /* undo effects of NOLIST for any following output to listing file. */ - listfile = listsave; - - /* Display value of error counter. */ - if( errors == 0 ) - { - fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); - } - else - { - fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - fprintf( listfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - fprintf( stderr, " %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - } - - if( symtab_print ) - { - printSymbolTable(); - } - - if( print_permanent_symbols ) - { - printPermanentSymbolTable(); - } - - if( xref ) - { - printCrossReference(); - } - - fclose( objectfile ); - fclose( listfile ); - fclose( errorfile ); - if( errors == 0 && errors_pass_1 == 0 ) - { - remove( errorpathname ); - } - - return( errors != 0 ); -} /* main() */ - -/******************************************************************************/ -/* */ -/* Function: getArgs */ -/* */ -/* Synopsis: Parse command line, set flags accordingly and setup input and */ -/* output files. */ -/* */ -/******************************************************************************/ -void getArgs( int argc, char *argv[] ) -{ - WORD32 len; - WORD32 ix, jx; - - /* Set the defaults */ - errorfile = NULL; - infile = NULL; - listfile = NULL; - listsave = NULL; - objectfile = NULL; - objectsave = NULL; - - for( ix = 1; ix < argc; ix++ ) - { - if( argv[ix][0] == '-' ) - { - for( jx = 1; argv[ix][jx] != 0; jx++ ) - { - switch( argv[ix][jx] ) - { - case 'd': - symtab_print = TRUE; - break; - -/* case 'r': - rim_mode = TRUE; - break; -*/ - case 'm': - nomac_exp = FALSE; - break; - - case 'p': - print_permanent_symbols = TRUE; - break; - - case 'x': - xref = TRUE; - break; - - default: - fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); - fprintf( stderr, " -d -- dump symbol table\n" ); - fprintf( stderr, " -m -- output macro expansions\n" ); -/* fprintf( stderr, " -r -- output rim format file\n" ); */ - fprintf( stderr, " -p -- output permanent symbols to file\n" ); - fprintf( stderr, " -x -- output cross reference to file\n" ); - fflush( stderr ); - exit( -1 ); - } /* end switch */ - } /* end for */ - } - else - { - filix_start = ix; - pathname = argv[ix]; - break; - } - } /* end for */ - - if( pathname == NULL ) - { - fprintf( stderr, "%s: no input file specified\n", argv[0] ); - exit( -1 ); - } - - len = strlen( pathname ); - if( len > NAMELEN - 5 ) - { - fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); - exit( -1 ); - } - - /* Now make the pathnames */ - /* Find last '.', if it exists. */ - jx = len - 1; - while( pathname[jx] != '.' && pathname[jx] != '/' - && pathname[jx] != '\\' && jx >= 0 ) - { - jx--; - } - - switch( pathname[jx] ) - { - case '.': - break; - - case '/': - case '\\': - jx = len; - break; - - default: - break; - } - - /* Add the pathname extensions. */ - strncpy( objectpathname, pathname, jx ); - objectpathname[jx] = '\0'; - strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); - - strncpy( listpathname, pathname, jx ); - listpathname[jx] = '\0'; - strcat( listpathname, ".lst" ); - - strncpy( errorpathname, pathname, jx ); - errorpathname[jx] = '\0'; - strcat( errorpathname, ".err" ); - - strncpy( permpathname, pathname, jx ); - permpathname[jx] = '\0'; - strcat( permpathname, ".prm" ); - - /* Extract the filename from the path. */ - if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) - { - pathname[1] = '\\'; /* MS-DOS style pathname */ - } - - jx = len - 1; - while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) - { - jx--; - } - strcpy( filename, &pathname[jx + 1] ); - -} /* getArgs() */ - - -/******************************************************************************/ -/* */ -/* Function: onePass */ -/* */ -/* Synopsis: Do one assembly pass. */ -/* */ -/******************************************************************************/ -void onePass() -{ - BOOL blanks; - int ix; - int jx; - char name[SYMLEN]; - WORD32 newclc; - BOOL scanning_line; - WORD32 start; - SYM_T *sym; - WORD32 term; - WORD32 val; - - clc = 0100; /* Default starting address is 100 octal. */ - start_addr = 0100; /* No starting address. */ - lit_loc = LIT_BASE; /* Literal pool base. */ - mac_count = 0; /* No macros defined. */ - mac_ptr = NULL; /* Not in a macro. */ - for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) - { - if ( mac_bodies[ix] ) - { - free( mac_bodies[ix] ); - } - } - cp.error = FALSE; - listed = TRUE; - lineno = 0; - list_pageno = 0; - list_lineno = 0; - list_title_set = FALSE; - page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ - radix = 8; /* Initial radix is octal (base 8). */ - - /* Now open the first input file. */ - end_of_input = FALSE; - filix_curr = filix_start; /* Initialize pointer to input files. */ - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) - { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], - save_argv[filix_curr] ); - exit( -1 ); - } - - while( TRUE ) - { - readLine(); - nextLexeme(); - - scanning_line = TRUE; - while( scanning_line ) - { - if( end_of_input ) - { - endOfBinary(); - fclose( infile ); - return; - } - if( isend( line[lexstart] )) - { - scanning_line = FALSE; - } - else - { - switch( line[lexstart] ) - { - case '/': - scanning_line = FALSE; - break; - - case '\t': - nextLexeme(); - break; - - default: - for( jx = lexstart; jx < maxcc; jx++ ) - { - if( is_blank( line[jx] ) || isdone( line[jx] )) break; - } - if( line[jx] == '/') - { - newclc = (getExpr())->val & 077777; - /* Do not change Current Location Counter if an error occurred. */ - if( !error_in_line ) - { - clc = newclc; - } - printLine( line, 0, newclc, LINE_VAL ); - cc = jx + 1; - nextLexeme(); - while( line[lexstart] == '\t' ) nextLexeme(); - break; - } - - switch( line[lexterm] ) - { - case ',': - if( isalpha( line[lexstart] )) - { - /* Use lookup so symbol will not be counted as reference. */ - sym = lookup( lexemeToName( name, lexstart, lexterm )); - if( M_DEFINED( sym->type )) - { - if( sym->val != clc && pass == 2 ) - { - errorSymbol( &duplicate_label, sym->name, lexstart ); - } - sym->type = sym->type | DUPLICATE; - } - /* Must call define on pass 2 to generate concordance. */ - defineLexeme( lexstart, lexterm, clc, LABEL ); - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - nextLexeme(); /* skip label */ - nextLexeme(); /* skip comma */ - while( line[lexstart] == '\t' ) nextLexeme(); - break; - - case '=': - if( isalpha( line[lexstart] )) - { - start = lexstart; - term = lexterm; - delimiter = line[lexterm]; - nextLexBlank(); /* skip symbol */ - nextLexeme(); /* skip trailing = */ - val = getExprs(); - defineLexeme( start, term, val, DEFINED ); - printLine( line, 0, val, LINE_VAL ); - } - else - { - errorLexeme( &symbol_syntax, lexstartprev ); - nextLexeme(); /* skip symbol */ - nextLexeme(); /* skip trailing = */ - getExprs(); /* skip expression */ - } - while( line[lexstart] == '\t' ) nextLexeme(); - break; - - default: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - val = sym->val; - if( M_MACRO( sym->type )) - { /* Find arguments. */ - blanks = TRUE; /* Expecting blanks. */ - for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) - { - if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; - else if( blanks ) - { - mac_arg_pos[jx++] = cc; - blanks = FALSE; - } - } /* end for */ - for( ; jx < MAC_MAX_ARGS; jx++ ) - { - mac_arg_pos[jx] = 0; - } - for( jx = 0; jx < LINELEN; jx++ ) - { - mac_line[jx] = line[jx]; - } - mac_cc = cc; /* Save line and position in line. */ - mac_ptr = mac_bodies[val]; - if( mac_ptr ) scanning_line = FALSE; - else nextLexeme(); - } /* end if macro */ - else if( M_PSEUDO( sym->type )) - { - nextLexeme(); /* Skip symbol */ - scanning_line = pseudoOperators( (PSEUDO_T)val & 0777777 ); - } - else - { - /* Identifier is not a pseudo-op, interpret as load value */ - punchOutObject( clc, getExprs() & 0777777 ); - incrementClc(); - } - } - else - { - /* Identifier is a value, interpret as load value */ - punchOutObject( clc, getExprs() & 0777777 ); - incrementClc(); - } - break; - } /* end switch */ - break; - } /* end switch */ - } /* end if */ - } /* end while( scanning_line ) */ - } /* end while( TRUE ) */ -} /* onePass() */ - - -/******************************************************************************/ -/* */ -/* Function: getExprs */ -/* */ -/* Synopsis: Or together a list of blank separated expressions, from the */ -/* current lexeme onward. Leave the current lexeme as */ -/* the last one in the list. */ -/* */ -/******************************************************************************/ -WORD32 getExprs() -{ - SYM_T *symv; - SYM_T *symt; - WORD32 temp; - SYMTYP temp_type; - WORD32 value; - SYMTYP value_type; - - symv = getExpr(); - value = symv->val; - value_type = symv->type; - - while( TRUE ) - { - if( isdone( line[lexstart] ) || line[lexstart] == ')' ) - { - return( value ); - } - - /* Interpret space as add */ - symt = getExpr(); - temp = symt->val & 0777777; - temp_type = symt->type; - - switch( value_type ) - { - case MRI: - case MRIFIX: - /* Previous symbol was a Memory Reference Instruction. */ - switch( temp_type ) - { - case MRI: - case MRIFIX: - /* Current symbol is also a Memory Reference Instruction. */ - value |= temp; /* Just OR the MRI instructions. */ - break; - - default: - /* Now have the address part of the MRI instruction. */ - if(( clc & 060000) == ( temp & 060000)) - { - value += ( temp & ADDRESS_FIELD ); /* In range MRI. */ - } - else - { - if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) - { - /* Already indirect, can't generate */ - errorSymbol( &illegal_indirect, symt->name, lexstartprev ); - } - else - { - /* Now fix off page reference. */ - /* Search current page literal pool for needed value. */ - /* Set Indirect */ - value += ( INDIRECT_BIT | insertLiteral( &cp, clc, temp & 077777)); - indirect_generated = TRUE; - } - } - break; - } - break; - - default: - value = value + temp; /* Normal 18 bit value. */ - if( value >= 0777777 ) value = ( value + 1 ) & 0777777; - break; - } - } /* end while */ -} /* getExprs() */ - - -/******************************************************************************/ -/* */ -/* Function: getExpr */ -/* */ -/* Synopsis: Get an expression, from the current lexeme onward, leave the */ -/* current lexeme as the one after the expression. Expressions */ -/* contain terminal symbols (identifiers) separated by operators. */ -/* */ -/******************************************************************************/ -SYM_T *getExpr() -{ - delimiter = line[lexterm]; - - if( line[lexstart] == '-' ) - { - nextLexBlank(); - sym_getexpr = *(eval()); - sym_getexpr.val = sym_getexpr.val ^ 0777777; - } - else - { - sym_getexpr = *(eval()); - } - - - if( is_blank( delimiter )) - { - return( &sym_getexpr ); - } - - /* Here we assume the current lexeme is the operator separating the */ - /* previous operator from the next, if any. */ - while( TRUE ) - { - /* assert line[lexstart] == delimiter */ - if( is_blank( delimiter )) - { - return( &sym_getexpr ); - } - - switch( line[lexstart] ) - { - case '+': /* add */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val += (eval())->val; - if( sym_getexpr.val >= 01000000 ) - { - sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; - } - break; - - case '-': /* subtract */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val = sym_getexpr.val + - ( (eval())->val ^ 0777777 ); - if( sym_getexpr.val >= 01000000 ) - { - sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; - } - break; - - case '^': /* multiply */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val *= (eval())->val; - break; - - case '%': /* divide */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val /= (eval())->val; - break; - - case '&': /* and */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val &= (eval())->val; - break; - - case '!': /* or */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val |= (eval())->val; - break; - - default: - if( isend( line[lexstart] )) - { - return( &sym_getexpr ); - } - - switch( line[lexstart] ) - { - case '/': - case '\t': - case ')': - case '<': - case ':': - case ',': - break; - - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - sym_getexpr.val = 0; - break; - - default: - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - sym_getexpr.val = 0; - break; - } - return( &sym_getexpr ); - } - } /* end while */ -} /* getExpr() */ - - -/******************************************************************************/ -/* */ -/* Function: eval */ -/* */ -/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ -/* */ -/******************************************************************************/ -SYM_T *eval() -{ - WORD32 digit; - WORD32 from; - WORD32 loc; - SYM_T *sym; - WORD32 val; - - val = 0; - - delimiter = line[lexterm]; - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - if( M_UNDEFINED( sym->type )) - { - if( pass == 2 ) - { - errorSymbol( &undefined_symbol, sym->name, lexstart ); - } - nextLexeme(); - return( sym ); - } - else if( M_PSEUDO( sym->type )) - { - if( sym->val == DECIMAL ) - { - radix = 10; - } - else if( sym->val == OCTAL ) - { - radix = 8; - } - else if( pass == 2 ) - { - errorSymbol( &misplaced_symbol, sym->name, lexstart ); - } - sym_eval.type = sym->type; - sym_eval.val = 0; - nextLexeme(); - return( &sym_eval ); - } - else if( M_MACRO( sym->type )) - { - if( pass == 2 ) - { - errorSymbol( &misplaced_symbol, sym->name, lexstart ); - } - sym_eval.type = sym->type; - sym_eval.val = 0; - nextLexeme(); - return( &sym_eval ); - } - else - { - nextLexeme(); - return( sym ); - } - } - else if( isdigit( line[lexstart] )) - { - from = lexstart; - val = 0; - while( from < lexterm ) - { - if( isdigit( line[from] )) - { - digit = (WORD32) line[from++] - (WORD32) '0'; - if( digit < radix ) - { - val = val * radix + digit; - } - else - { - errorLexeme( &number_not_radix, from - 1 ); - val = 0; - from = lexterm; - } - } - else - { - errorLexeme( ¬_a_number, lexstart ); - val = 0; - from = lexterm; - } - } - nextLexeme(); - sym_eval.val = val; - return( &sym_eval ); - } - else - { - switch( line[lexstart] ) - { - case '"': /* Character literal */ - if( cc + 2 < maxcc ) - { - val = line[lexstart + 1] | 0200; - delimiter = line[lexstart + 2]; - cc = lexstart + 2; - } - else - { - errorMessage( &no_literal_value, lexstart ); - } - nextLexeme(); - break; - - case '.': /* Value of Current Location Counter */ - val = clc; - nextLexeme(); - break; - - case '(': /* Generate literal on current page. */ - nextLexBlank(); /* Skip paren */ - val = getExprs() & 0777777; - - if( line[lexstart] == ')' ) - { - delimiter = line[lexterm]; - nextLexeme(); /* Skip end paren */ - } - else - { - /* errorMessage( "parens", NULL ); */ - } - - loc = insertLiteral( &cp, clc, val ); - sym_eval.val = loc + ( clc & 060000 ); - return( &sym_eval ); - - default: - switch( line[lexstart] ) - { - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - break; - - default: - errorMessage( &illegal_character, lexstart ); - break; - } - val = 0; /* On error, set value to zero. */ - nextLexBlank(); /* Go past illegal character. */ - } - } - sym_eval.val = val; - return( &sym_eval ); -} /* eval() */ - -/******************************************************************************/ -/* */ -/* Function: incrementClc */ -/* */ -/* Synopsis: Set the next assembly location. Test for collision with */ -/* the literal tables. */ -/* */ -/******************************************************************************/ -WORD32 incrementClc() -{ - testForLiteralCollision( clc ); - clc = (( clc + 1 ) & ADDRESS_FIELD ); - return( clc ); -} /* incrementClc() */ - - -/******************************************************************************/ -/* */ -/* Function: testForLiteralCollision */ -/* */ -/* Synopsis: Test the given location for collision with the literal tables. */ -/* */ -/******************************************************************************/ -BOOL testForLiteralCollision( WORD32 loc ) -{ - WORD32 pagelc; - BOOL result = FALSE; - - pagelc = loc & ADDRESS_FIELD; - if( ( pagelc >= lit_loc ) && ( lit_loc != LIT_BASE ) && !cp.error ) - { - errorMessage( &literal_overflow, -1 ); - cp.error = TRUE; - result = TRUE; - } - return( result ); -} /* testForLiteralCollision() */ - - -/******************************************************************************/ -/* */ -/* Function: readLine */ -/* */ -/* Synopsis: Get next line of input. Print previous line if needed. */ -/* */ -/******************************************************************************/ -void readLine() -{ - BOOL ffseen; - WORD32 ix; - WORD32 iy; - char mc; - char inpline[4*LINELEN]; - - listLine(); /* List previous line if needed. */ - indirect_generated = FALSE; /* Mark no indirect address generated. */ - error_in_line = FALSE; /* No error in line. */ - - if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ - { - mac_ptr = NULL; - for( ix = 0; ix < LINELEN; ix++ ) - { - line[ix] = mac_line[ix]; /* Restore invoking line. */ - } - cc = lexstartprev = mac_cc; /* Restore cc. */ - maxcc = strlen( line ); /* Restore maxcc. */ - listed = TRUE; /* Already listed. */ - return; - } - - cc = 0; /* Initialize column counter. */ - lexstartprev = 0; - if( mac_ptr ) /* Inside macro? */ - { - maxcc = 0; - do - { - mc = *mac_ptr++; /* Next character. */ - if( islower( mc )) /* Encoded argument number? */ - { - ix = mc - 'a'; /* Convert to index. */ - if( iy = mac_arg_pos[ix] ) - { - do /* Copy argument string. */ - { - line[maxcc++] = mac_line[iy++]; - } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && - ( !isdone( mac_line[iy] ))); - } - } - else /* Ordinary character, just copy. */ - { - line[maxcc++] = mc; - } - } while( !isend( mc )); - line[maxcc] = '\0'; - listed = nomac_exp; - return; - } - - lineno++; /* Count lines read. */ - listed = FALSE; /* Mark as not listed. */ -READ_LINE: - if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) - { - filix_curr++; /* Advance to next file. */ - if( filix_curr < save_argc ) /* More files? */ - { - fclose( infile ); - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) - { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], - save_argv[filix_curr] ); - exit( -1 ); - } - goto READ_LINE; - } - else - { - end_of_input = TRUE; - } - } - - ffseen = FALSE; - for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) - { - if( inpline[ix] == '\f' ) - { - if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); - ffseen = TRUE; - } - else - { - line[iy++] = inpline[ix]; - } - } - line[iy] = '\0'; - - /* If the line is terminated by CR-LF, remove, the CR. */ - if( line[iy - 2] == '\r' ) - { - iy--; - line[iy - 1] = line[iy - 0]; - line[iy] = '\0'; - } - maxcc = iy; /* Save the current line length. */ -} /* readLine() */ - - -/******************************************************************************/ -/* */ -/* Function: listLine */ -/* */ -/* Synopsis: Output a line to the listing file. */ -/* */ -/******************************************************************************/ -void listLine() -/* generate a line of listing if not already done! */ -{ - if( listfile != NULL && listed == FALSE ) - { - printLine( line, 0, 0, LINE ); - } -} /* listLine() */ - - -/******************************************************************************/ -/* */ -/* Function: printPageBreak */ -/* */ -/* Synopsis: Output a Top of Form and listing header if new page necessary. */ -/* */ -/******************************************************************************/ -void printPageBreak() -{ - if( page_lineno >= LIST_LINES_PER_PAGE ) - /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ - { - if( !list_title_set ) - { - strcpy( list_title, line ); - if( list_title[strlen(list_title) - 1] == '\n' ) - { - list_title[strlen(list_title) - 1] = '\0'; - } - if( strlen( list_title ) > TITLELEN ) - { - list_title[TITLELEN] = '\0'; - } - list_title_set = TRUE; - } - topOfForm( list_title, NULL ); - - } -} /* printPageBreak() */ - - -/******************************************************************************/ -/* */ -/* Function: printLine */ -/* */ -/* Synopsis: Output a line to the listing file with new page if necessary. */ -/* */ -/******************************************************************************/ -void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) -{ - if( listfile == NULL ) - { - save_error_count = 0; - return; - } - - printPageBreak(); - - list_lineno++; - page_lineno++; - switch( linestyle ) - { - default: - case LINE: - fprintf( listfile, "%5d ", lineno ); - fputs( line, listfile ); - listed = TRUE; - break; - - case LINE_VAL: - if( !listed ) - { - fprintf( listfile, "%5d %6.6o ", lineno, val ); - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %6.6o\n", val ); - } - break; - - case LINE_LOC_VAL: - if( !listed ) - { - if( indirect_generated ) - { - fprintf( listfile, "%5d %5.5o %6.6o@ ", lineno, loc, val ); - } - else - { - fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); - } - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %5.5o %6.6o\n", loc, val ); - } - break; - - case LOC_VAL: - fprintf( listfile, " %5.5o %6.6o\n", loc, val ); - break; - } - printErrorMessages(); -} /* printLine() */ - - -/******************************************************************************/ -/* */ -/* Function: printErrorMessages */ -/* */ -/* Synopsis: Output any error messages from the current list of errors. */ -/* */ -/******************************************************************************/ -void printErrorMessages() -{ - WORD32 ix; - WORD32 iy; - - if( listfile != NULL ) - { - /* If any errors, display them now. */ - for( iy = 0; iy < save_error_count; iy++ ) - { - printPageBreak(); - fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); - if( error_list[iy].col >= 0 ) - { - for( ix = 0; ix < error_list[iy].col; ix++ ) - { - if( line[ix] == '\t' ) - { - putc( '\t', listfile ); - } - else - { - putc( ' ', listfile ); - } - } - fputs( "^", listfile ); - list_lineno++; - page_lineno++; - } - fputs( "\n", listfile ); - } - } - save_error_count = 0; -} /* printErrorMessages() */ - - -/******************************************************************************/ -/* */ -/* Function: endOfBinary */ -/* */ -/* Synopsis: Outputs both literal tables at the end of a binary segment. */ -/* */ -/******************************************************************************/ -void endOfBinary() -{ - punchLiteralPool( &cp, clc - 1 ); - if( start_addr >= 0) - { - punchTriplet( JMP | ( start_addr & 017777 )); - punchTriplet( 0 ); - } - return; -} /* endOfBinary() */ - - -/******************************************************************************/ -/* */ -/* Function: punchLeader */ -/* */ -/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ -/* documentation. Paper tape has 10 punches per inch. */ -/* */ -/******************************************************************************/ -void punchLeader( WORD32 count ) -{ - WORD32 ix; - - /* If value is zero, set to the default of 2 feet of leader. */ - count = ( count == 0 ) ? 240 : count; - - if( objectfile != NULL ) - { - for( ix = 0; ix < count; ix++ ) - { - fputc( 0, objectfile ); - } - } -} /* punchLeader() */ - - -/******************************************************************************/ -/* */ -/* Function: punchObject */ -/* */ -/* Synopsis: Put one character to object file and include it in checksum. */ -/* */ -/******************************************************************************/ -void punchObject( WORD32 val ) -{ - val &= 0377; - if( objectfile != NULL ) - { - fputc( val, objectfile ); - } - checksum += val; - binary_data_output = TRUE; -} /* punchObject() */ - - -/******************************************************************************/ -/* */ -/* Function: punchOutObject */ -/* */ -/* Synopsis: Output the current line and then then punch value to the */ -/* object file. */ -/* */ -/******************************************************************************/ -void punchOutObject( WORD32 loc, WORD32 val ) -{ - printLine( line, loc, val, LINE_LOC_VAL ); - punchLocObject( loc, val ); -} /* punchOutObject() */ - - -/******************************************************************************/ -/* */ -/* Function: punchLocObject */ -/* */ -/* Synopsis: Output the word (with origin if rim format) to the object file.*/ -/* */ -/******************************************************************************/ -void punchLocObject( WORD32 loc, WORD32 val ) -{ - punchTriplet( DAC | loc ); - punchTriplet( val ); -} /* punchLocObject() */ - - -/******************************************************************************/ -/* */ -/* Function: punchTriplet */ -/* */ -/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ -/* */ -/******************************************************************************/ -void punchTriplet( WORD32 val ) -{ - punchObject((( val >> 12) & 077) | 0200 ); - punchObject((( val >> 6 ) & 077) | 0200 ); - punchObject(( val & 077) | 0200 ); -} /* punchTriplet */ - - -/******************************************************************************/ -/* */ -/* Function: punchLiteralPool */ -/* */ -/* Synopsis: Output the current page data. */ -/* */ -/******************************************************************************/ -void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) -{ - WORD32 loc; - WORD32 tmplc; - - if( lit_loc < LIT_BASE ) - { - for( loc = lit_loc; loc < LIT_BASE; loc++ ) - { - tmplc = loc + ( lpool_page & 060000 ); - printLine( line, tmplc, p->pool[loc], LOC_VAL ); - punchLocObject( tmplc, p->pool[loc] ); - } - p->error = FALSE; - lit_loc = LIT_BASE; - } -} /* punchLiteralPool() */ - - -/******************************************************************************/ -/* */ -/* Function: insertLiteral */ -/* */ -/* Synopsis: Add a value to the given literal pool if not already in pool. */ -/* Return the location of the value in the pool. */ -/* */ -/******************************************************************************/ -WORD32 insertLiteral( LPOOL_T *p, WORD32 pool_page, WORD32 value ) -{ - WORD32 ix; - - /* Search the literal pool for any occurence of the needed value. */ - ix = LIT_BASE - 1; - while( ix >= lit_loc && p->pool[ix] != value ) - { - ix--; - } - - /* Check if value found in literal pool. If not, then insert value. */ - if( ix < lit_loc ) - { - lit_loc--; - p->pool[lit_loc] = value; - ix = lit_loc; - } - return( ix ); -} /* insertLiteral() */ - - -/******************************************************************************/ -/* */ -/* Function: printSymbolTable */ -/* */ -/* Synopsis: Output the symbol table. */ -/* */ -/******************************************************************************/ -void printSymbolTable() -{ - int col; - int cx; - char *fmt; - int ix; - char mark; - int page; - int row; - int symbol_base; - int symbol_lines; - - symbol_base = number_of_fixed_symbols; - - for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) - { - topOfForm( list_title, s_symtable ); - symbol_lines = LIST_LINES_PER_PAGE - page_lineno; - - for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) - { - list_lineno++; - page_lineno++; - fprintf( listfile, "%5d", list_lineno ); - - for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) - { - /* Get index of symbol for the current line and column */ - cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; - cx += symbol_base; - - /* Make sure that there is a symbol to be printed. */ - if( number_of_fixed_symbols <= cx && cx < symbol_top ) - { - switch( symtab[cx].type & LABEL ) - { - case LABEL: - fmt = " %c%-6.6s %5.5o "; - break; - - default: - fmt = " %c%-6.6s %4.4o "; - break; - } - - switch( symtab[cx].type & ( DEFINED | REDEFINED )) - { - case UNDEFINED: - mark = '?'; - break; - - case REDEFINED: - mark = '#'; - break; - - default: - mark = ' '; - break; - } - fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); - ix++; - } - } - fprintf( listfile, "\n" ); - } - } -} /* printSymbolTable() */ - - -/******************************************************************************/ -/* */ -/* Function: printPermanentSymbolTable */ -/* */ -/* Synopsis: Output the permanent symbol table to a file suitable for */ -/* being input after the EXPUNGE pseudo-op. */ -/* */ -/******************************************************************************/ -void printPermanentSymbolTable() -{ - int ix; - FILE *permfile; - char *s_type; - - if(( permfile = fopen( permpathname, "w" )) == NULL ) - { - exit( 2 ); - } - - fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); - fprintf( permfile, " EXPUNGE\n/\n" ); - /* Print the memory reference instructions first. */ - s_type = "FIXMRI"; - for( ix = 0; ix < symbol_top; ix++ ) - { - if( M_MRI( symtab[ix].type )) - { - fprintf( permfile, "%-7s %s=%4.4o\n", - s_type, symtab[ix].name, symtab[ix].val ); - } - } - - s_type = " "; - for( ix = 0; ix < symbol_top; ix++ ) - { - if( M_FIXED( symtab[ix].type )) - { - if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) - { - fprintf( permfile, "%-7s %s=%4.4o\n", - s_type, symtab[ix].name, symtab[ix].val ); - } - } - } - fprintf( permfile, "/\n FIXTAB\n" ); - fclose( permfile ); -} /* printPermanentSymbolTable() */ - - -/******************************************************************************/ -/* */ -/* Function: printCrossReference */ -/* */ -/* Synopsis: Output a cross reference (concordance) for the file being */ -/* assembled. */ -/* */ -/******************************************************************************/ -void printCrossReference() -{ - int ix; - int symbol_base; - int xc; - int xc_index; - int xc_refcount; - int xc_cols; - - /* Force top of form for first page. */ - page_lineno = LIST_LINES_PER_PAGE; - - list_lineno = 0; - symbol_base = number_of_fixed_symbols; - - for( ix = symbol_base; ix < symbol_top; ix++ ) - { - list_lineno++; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - { - topOfForm( list_title, s_xref ); - } - - fprintf( listfile, "%5d", list_lineno ); - - /* Get reference count & index into concordance table for this symbol. */ - xc_refcount = symtab[ix].xref_count; - xc_index = symtab[ix].xref_index; - /* Determine how to label symbol on concordance. */ - switch( symtab[ix].type & ( DEFINED | REDEFINED )) - { - case UNDEFINED: - fprintf( listfile, " U "); - break; - - case REDEFINED: - fprintf( listfile, " M %5d ", xreftab[xc_index] ); - break; - - default: - fprintf( listfile, " A %5d ", xreftab[xc_index] ); - break; - } - fprintf( listfile, "%-6.6s ", symtab[ix].name ); - - /* Output the references, 8 numbers per line after symbol name. */ - for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) - { - if( xc_cols >= XREF_COLUMNS ) - { - xc_cols = 0; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - { - topOfForm( list_title, s_xref); - } - list_lineno++; - fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); - } - fprintf( listfile, " %5d", xreftab[xc_index + xc] ); - } - fprintf( listfile, "\n" ); - } -} /* printCrossReference() */ - - -/******************************************************************************/ -/* */ -/* Function: topOfForm */ -/* */ -/* Synopsis: Prints title and sub-title on top of next page of listing. */ -/* */ -/******************************************************************************/ -void topOfForm( char *title, char *sub_title ) -{ - char temp[10]; - - list_pageno++; - strcpy( temp, s_page ); - sprintf( temp, "%s %d", s_page, list_pageno ); - - /* Output a top of form if not the first page of the listing. */ - if( list_pageno > 1 ) - { - fprintf( listfile, "\f" ); - } - fprintf( listfile, "\n %-63s %10s\n", title, temp ); - - /* Reset the current page line counter. */ - page_lineno = 1; - if( sub_title != NULL ) - { - fprintf( listfile, "%80s\n", sub_title ); - page_lineno++; - } - else - { - fprintf( listfile, "\n" ); - page_lineno++; - } - fprintf( listfile, "\n" ); - page_lineno++; -} /* topOfForm() */ - - -/******************************************************************************/ -/* */ -/* Function: lexemeToName */ -/* */ -/* Synopsis: Convert the current lexeme into a string. */ -/* */ -/******************************************************************************/ -char *lexemeToName( char *name, WORD32 from, WORD32 term ) -{ - WORD32 to; - - to = 0; - - while( from < term && to < ( SYMLEN - 1 )) - { - name[to++] = toupper( line[from++] ); - } - - while( to < SYMLEN ) - { - name[to++] = '\0'; - } - return( name ); -} /* lexemeToName() */ - -/******************************************************************************/ -/* */ -/* Function: defineLexeme */ -/* */ -/* Synopsis: Put lexeme into symbol table with a value. */ -/* */ -/******************************************************************************/ -SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ - WORD32 term, /* end+1 of lexeme being defined. */ - WORD32 val, /* value of lexeme being defined. */ - SYMTYP type ) /* how symbol is being defined. */ -{ - char name[SYMLEN]; - - lexemeToName( name, start, term); - return( defineSymbol( name, val, type, start )); -} /* defineLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: defineSymbol */ -/* */ -/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ -/* not already in table. */ -/* */ -/******************************************************************************/ -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) -{ - SYM_T *sym; - WORD32 xref_count; - - if( strlen( name ) < 1 ) - { - return( &sym_undefined ); /* Protect against non-existent names. */ - } - sym = lookup( name ); - xref_count = 0; /* Set concordance for normal defintion. */ - - if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) - { - if( pass == 2 ) - { - errorSymbol( &redefined_symbol, sym->name, start ); - type = type | REDEFINED; - sym->xref_count++; /* Referenced symbol, count it. */ - xref_count = sym->xref_count; - } - return ( sym ); - } - - if( pass == 2 && xref ) - { - /* Put the definition line number in the concordance table. */ - /* Defined symbols are not counted as references. */ - xreftab[sym->xref_index] = lineno; - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + xref_count] = lineno; - } - - /* Now set the value and the type. */ - sym->val = ( type == LABEL) ? val : val & 0777777; - sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; - return( sym ); -} /* defineSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: lookup */ -/* */ -/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ -/* table as undefined. Return address of symbol in table. */ -/* */ -/******************************************************************************/ -SYM_T *lookup( char *name ) -{ - int ix; /* Insertion index */ - int lx; /* Left index */ - int rx; /* Right index */ - - /* First search the permanent symbols. */ - lx = 0; - ix = binarySearch( name, lx, number_of_fixed_symbols ); - - /* If symbol not in permanent symbol table. */ - if( ix < 0 ) - { - /* Now try the user symbol table. */ - ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); - - /* If symbol not in user symbol table. */ - if( ix < 0 ) - { - /* Must put symbol in table if index is negative. */ - ix = ~ix; - if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) - { - errorSymbol( &symbol_table_full, name, lexstart ); - exit( 1 ); - } - - for( rx = symbol_top; rx >= ix; rx-- ) - { - symtab[rx + 1] = symtab[rx]; - } - symbol_top++; - - /* Enter the symbol as UNDEFINED with a value of zero. */ - strcpy( symtab[ix].name, name ); - symtab[ix].type = UNDEFINED; - symtab[ix].val = 0; - symtab[ix].xref_count = 0; - if( xref && pass == 2 ) - { - xreftab[symtab[ix].xref_index] = 0; - } - } - } - - return( &symtab[ix] ); /* Return the location of the symbol. */ -} /* lookup() */ - - -/******************************************************************************/ -/* */ -/* Function: binarySearch */ -/* */ -/* Synopsis: Searches the symbol table within the limits given. If the */ -/* symbol is not in the table, it returns the insertion point. */ -/* */ -/******************************************************************************/ -int binarySearch( char *name, int start, int symbol_count ) -{ - int lx; /* Left index */ - int mx; /* Middle index */ - int rx; /* Right index */ - int compare; /* Results of comparison */ - - lx = start; - rx = symbol_count - 1; - while( lx <= rx ) - { - mx = ( lx + rx ) / 2; /* Find center of search area. */ - - compare = strcmp( name, symtab[mx].name ); - - if( compare < 0 ) - { - rx = mx - 1; - } - else if( compare > 0 ) - { - lx = mx + 1; - } - else - { - return( mx ); /* Found a match in symbol table. */ - } - } /* end while */ - return( ~lx ); /* Return insertion point. */ -} /* binarySearch() */ - - -/******************************************************************************/ -/* */ -/* Function: compareSymbols */ -/* */ -/* Synopsis: Used to presort the symbol table when starting assembler. */ -/* */ -/******************************************************************************/ -int compareSymbols( const void *a, const void *b ) -{ - return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); -} /* compareSymbols() */ - -/******************************************************************************/ -/* */ -/* Function: copyMacLine */ -/* */ -/* Synopsis: Used to copy a macro line to the macro buffer. */ -/* */ -/******************************************************************************/ -int copyMacLine( int length, int from, int term, int nargs ) -{ - char name[SYMLEN]; - int ix; - int jx; - int kx; - BOOL bl; - - bl = TRUE; - for( ix = from; ix < term; ix++ ) - { - if( !is_blank( line[ix] ) || ( line[ix] == '\t' )) bl = FALSE; - } - if( bl || ( length < 0 )) return length; - if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; - for( ix = from; ix < term; ) - { - if( nargs && isalpha( line[ix] )) /* Start of symbol? */ - { - for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ - { - if( !isalnum( line[jx] )) break; - } - lexemeToName( name, ix, jx ); /* Make into name. */ - for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ - { - if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) - { - mac_buffer[length++] = 'a' + (char) kx; - for( ix++; ix < jx; ix++ ) - { - mac_buffer[length++] = 'z'; - } - break; - } /* end if strncmp */ - } /* end for kx */ - if( kx >= nargs ) - { - for ( ; ix < jx; ) - { - mac_buffer[length++] = toupper( line[ix++] ); - } - } - } /*end if nargs */ - else - { - mac_buffer[length++] = toupper( line[ix++] ); - } /* end else */ - } /* end for ix */ - mac_buffer[length++] = '\n'; - mac_buffer[length] = 0; - return length; -} - -/******************************************************************************/ -/* */ -/* Function: evalSymbol */ -/* */ -/* Synopsis: Get the pointer for the symbol table entry if exists. */ -/* If symbol doesn't exist, return a pointer to the undefined sym */ -/* */ -/******************************************************************************/ -SYM_T *evalSymbol() -{ - char name[SYMLEN]; - SYM_T *sym; - - sym = lookup( lexemeToName( name, lexstart, lexterm )); - - sym->xref_count++; /* Count the number of references to symbol. */ - - if( xref && pass == 2 ) - { - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + sym->xref_count] = lineno; - } - - return( sym ); -} /* evalSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: moveToEndOfLine */ -/* */ -/* Synopsis: Move the parser input to the end of the current input line. */ -/* */ -/******************************************************************************/ -void moveToEndOfLine() -{ - while( !isend( line[cc] )) cc++; - lexstart = cc; - lexterm = cc; - lexstartprev = lexstart; -} /* moveToEndOfLine() */ - -/******************************************************************************/ -/* */ -/* Function: nextLexeme */ -/* */ -/* Synopsis: Get the next lexical element from input line. */ -/* */ -/******************************************************************************/ -void nextLexeme() -{ - /* Save start column of previous lexeme for diagnostic messages. */ - lexstartprev = lexstart; - lextermprev = lexterm; - - while( is_blank( line[cc] ) || - (( line[cc] == '\t' ) && ( line[cc+1] == '\t' )) || - (( line[cc] == '\t' ) && isdone( line[cc+1] ))) { cc++; } - lexstart = cc; - - if( isalnum( line[cc] )) - { - while( isalnum( line[cc] )) { cc++; } - } - else if( isend( line[cc] )) - { - /* End-of-Line, don't advance cc! */ - } - else - { - switch( line[cc] ) - { - case '"': /* Quoted letter */ - if( cc + 2 < maxcc ) - { - cc++; - cc++; - } - else - { - errorMessage( &no_literal_value, lexstart ); - cc++; - } - break; - - case '/': /* Comment, don't advance cc! */ - break; - - default: /* All other punctuation. */ - cc++; - break; - } - } - lexterm = cc; -} /* nextLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: nextLexBlank */ -/* */ -/* Synopsis: Used to prevent illegal blanks in expressions. */ -/* */ -/******************************************************************************/ -void nextLexBlank() -{ - nextLexeme(); - if( is_blank( delimiter )) - { - errorMessage( &illegal_blank, lexstart - 1 ); - } - delimiter = line[lexterm]; -} /* nextLexBlank() */ - - - -/******************************************************************************/ -/* */ -/* Function: pseudoOperators */ -/* */ -/* Synopsis: Process pseudo-ops (directives). */ -/* */ -/******************************************************************************/ -BOOL pseudoOperators( PSEUDO_T val ) -{ - int count; - int delim; - int index; - int ix; - WORD32 length; - int level; - int lexstartsave; - int pack; - int pos; - int radixprev; - BOOL status; - SYM_T *sym; - WORD32 value; - WORD32 word; - int width; - static int mask_tab[19] = { 0000000, - 0000001, 0000003, 0000007, 0000017, 0000037, 0000077, - 0000177, 0000377, 0000777, 0001777, 0003777, 0007777, - 0017777, 0037777, 0077777, 0177777, 0377777, 0777777 }; - - status = TRUE; - switch( (PSEUDO_T) val ) - { - case DECIMAL: - radix = 10; - break; - - case DEFINE: - count = 0; - index = 0; - lexstartsave = lexstart; - while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && - ( count < MAC_MAX_ARGS )) - { - if ( !isalpha( line[lexstart] ) && ( index == 0 )) - { - index = lexstart; - } - lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); - nextLexeme(); - } - if( count == 0 ) /* No macro name. */ - { - errorMessage( &no_macro_name, lexstartsave ); - index = 1; - } - else if( index ) /* Bad argument name. */ - { - errorMessage( &bad_dummy_arg, index ); - } - else if( mac_count >= MAC_TABLE_LENGTH ) - { - errorMessage( ¯o_table_full, lexstartsave ); - index = 1; - } - else - { - value = mac_count; - mac_count++; /* Value is entry in mac_bodies. */ - defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); - } - if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) - { - readLine(); - nextLexeme(); - } - if( index ) - { - conditionFalse(); /* On error skip macro body. */ - } - else if( line[lexstart] == '<' ) - { - /* Invariant: line[cc] is the next unexamined character. */ - index = lexstart + 1; - length = 0; - level = 1; - while( level > 0 ) - { - if( end_of_input ) - { - break; - } - if( isend( line[cc] ) || ( line[cc] == '/' )) - { - length = copyMacLine( length, index, cc, count - 1 ); - readLine(); - index = 0; - } - else - { - switch( line[cc] ) - { - case '>': - level--; - cc++; - break; - - case '<': - level++; - cc++; - break; - - default: - cc++; - break; - } /* end switch */ - } /* end if */ - } /* end while */ - length = copyMacLine( length, index, cc - 1, count - 1 ); - if( length < 0 ) - { - errorMessage (¯o_too_long, lexstart ); - } - else if( length == 0 ) - { - mac_bodies[value] = NULL; - } - else - { - mac_bodies[value] = (char *) malloc( length + 1 ); - if( mac_bodies[value] ) - { - strncpy( mac_bodies[value], mac_buffer, length ); - *( mac_bodies[value] + length ) = 0; - } - else - { - errorMessage( &no_virtual_memory, lexstart ); - } - } - nextLexeme(); - } - else - { - errorMessage( <_expected, lexstart ); - } /* end if */ - break; - - case EJECT: - page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ - status = FALSE; /* This will force reading of next line */ - break; - - case IFDEF: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - nextLexeme(); - if( M_DEFINED_CONDITIONALLY( sym->type )) - { - conditionTrue(); - } - else - { - conditionFalse(); - } - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - break; - - case IFNDEF: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - nextLexeme(); - if( M_DEFINED_CONDITIONALLY( sym->type )) - { - conditionFalse(); - } - else - { - conditionTrue(); - } - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - break; - - case IFNZERO: - if( (getExpr())->val == 0 ) - { - conditionFalse(); - } - else - { - conditionTrue(); - } - break; - - case IFZERO: - if( (getExpr())->val == 0 ) - { - conditionTrue(); - } - else - { - conditionFalse(); - } - break; - - case LIST: - listfile = listsave; - break; - - case NOLIST: - listfile = NULL; - break; - - case OCTAL: - radix = 8; - break; - - case START: - if( !isdone( line[lexstart] )) - { - start_addr = (getExpr())->val & 077777; - nextLexeme(); - } - printLine( line, 0, start_addr, LINE_VAL ); - status = FALSE; - break; - - case TEXT: - delim = line[lexstart]; - pack = 0; - count = 0; - index = lexstart + 1; - while( line[index] != delim && !isend( line[index] )) - { - pack = ( pack << 6 ) | ( line[index] & 077 ); - count++; - if( count > 2 ) - { - punchOutObject( clc, pack ); - incrementClc(); - count = 0; - pack = 0; - } - index++; - } - - if( count == 2 ) - { - punchOutObject( clc, pack << 6 ); - } - else if (count == 1) - { - punchOutObject( clc, pack << 12 ); - } - else - { - punchOutObject( clc, 0 ); - } - incrementClc(); - - if( isend( line[index] )) - { - cc = index; - lexterm = cc; - errorMessage( &text_string, cc ); - } - else - { - cc = index + 1; - lexterm = cc; - } - nextLexeme(); - break; - - case TITLE: - delim = line[lexstart]; - ix = lexstart + 1; - /* Find string delimiter. */ - do - { - if( list_title[ix] == delim && list_title[ix + 1] == delim ) - { - ix++; - } - ix++; - } while( line[ix] != delim && !isend(line[ix]) ); - - if( !isend( line[ix] ) ) - { - count = 0; - ix = lexstart + 1; - do - { - if( list_title[ix] == delim && list_title[ix + 1] == delim ) - { - ix++; - } - list_title[count] = line[ix]; - count++; - ix++; - } while( line[ix] != delim && !isend(line[ix]) ); - - if( strlen( list_title ) > TITLELEN ) - { - list_title[TITLELEN] = '\0'; - } - - cc = ix + 1; - lexterm = cc; - page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ - list_title_set = TRUE; - } - else - { - cc = ix; - lexterm = cc; - errorMessage( &text_string, cc ); - } - - nextLexeme(); - break; - - case VFD: - pos = 0; - word = 0; - radixprev = radix; - while( !isdone (line[lexstart] )) - { - lexstartsave = lexstart; - radix = 10; - width = (getExpr())->val; /* Get field width. */ - radix = radixprev; - if( (width <= 0) || ((width + pos) > 18) || (line[lexstart] != ':') ) - { - errorMessage( &illegal_vfd_value, lexstartsave ); - } - nextLexBlank(); /* Skip colon. */ - value = (getExpr())->val; /* Get field value. */ - if( line[lexterm] == ',' ) cc++; - nextLexeme(); /* Advance to next field. */ - pos = pos + width; - if( pos <= 18 ) - { - word = word | ((value & mask_tab[width]) << (18 - pos)); - } - } - punchOutObject( clc, word ); - incrementClc(); - break; - - default: - break; - } /* end switch for pseudo-ops */ - return( status ); -} /* pseudoOperators() */ - - -/******************************************************************************/ -/* */ -/* Function: conditionFalse */ -/* */ -/* Synopsis: Called when a false conditional has been evaluated. */ -/* Lex should be the opening <; ignore all text until */ -/* the closing >. */ -/* */ -/******************************************************************************/ -void conditionFalse() -{ - int level; - - if( line[lexstart] == '<' ) - { - /* Invariant: line[cc] is the next unexamined character. */ - level = 1; - while( level > 0 ) - { - if( end_of_input ) - { - break; - } - if( isend( line[cc] ) || ( line[cc] == '/' )) - { - readLine(); - } - else - { - switch( line[cc] ) - { - case '>': - level--; - cc++; - break; - - case '<': - level++; - cc++; - break; - - default: - cc++; - break; - } /* end switch */ - } /* end if */ - } /* end while */ - nextLexeme(); - } - else - { - errorMessage( <_expected, lexstart ); - } -} /* conditionFalse() */ - -/******************************************************************************/ -/* */ -/* Function: conditionTrue */ -/* */ -/* Synopsis: Called when a true conditional has been evaluated. */ -/* Lex should be the opening <; skip it and setup for */ -/* normal assembly. */ -/* */ -/******************************************************************************/ -void conditionTrue() -{ - if( line[lexstart] == '<' ) - { - nextLexeme(); /* Skip the opening '<' */ - } - else - { - errorMessage( <_expected, lexstart ); - } -} /* conditionTrue() */ - - -/******************************************************************************/ -/* */ -/* Function: errorLexeme */ -/* */ -/* Synopsis: Display an error message using the current lexical element. */ -/* */ -/******************************************************************************/ -void errorLexeme( EMSG_T *mesg, WORD32 col ) -{ - char name[SYMLEN]; - - errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); -} /* errorLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: errorSymbol */ -/* */ -/* Synopsis: Display an error message with a given string. */ -/* */ -/******************************************************************************/ -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) -{ - char linecol[12]; - char *s; - - if( pass == 2 ) - { - s = ( name == NULL ) ? "" : name ; - errors++; - sprintf( linecol, "(%d:%d)", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", - filename, linecol, mesg->file, s, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: errorMessage */ -/* */ -/* Synopsis: Display an error message without a name argument. */ -/* */ -/******************************************************************************/ -void errorMessage( EMSG_T *mesg, WORD32 col ) -{ - char linecol[12]; - - if( pass == 2 ) - { - errors++; - sprintf( linecol, "(%d:%d)", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", - filename, linecol, mesg->file, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorMessage() */ - -/******************************************************************************/ -/* */ -/* Function: saveError */ -/* */ -/* Synopsis: Save the current error in a list so it may displayed after the */ -/* the current line is printed. */ -/* */ -/******************************************************************************/ -void saveError( char *mesg, WORD32 col ) -{ - if( save_error_count < DIM( error_list )) - { - error_list[save_error_count].mesg = mesg; - error_list[save_error_count].col = col; - save_error_count++; - } - error_in_line = TRUE; - - if( listed ) - { - printErrorMessages(); - } -} /* saveError() */ -/* End-of-File */ +/******************************************************************************/ +/* */ +/* Program: MACRO7 */ +/* File: macro7.c */ +/* Author: Gary A. Messenbrink */ +/* MACRO7 modifications: Bob Supnik (:) : error: at Loc = */ +/* */ +/* An example error message is: */ +/* */ +/* bintst.7(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ +/* */ +/* The error diagnostics put in the listing start with a two character */ +/* error code (if appropriate) and a short message. A carat '^' is */ +/* placed under the item in error if appropriate. */ +/* An example error message is: */ +/* */ +/* 17 07616 3000 DAC UNDEF */ +/* UD undefined ^ */ +/* 18 07617 1777 TAD I DUMMY */ +/* */ +/* When an indirect is generated, an at character '@' is placed after the */ +/* the instruction value in the listing as an indicator as follows: */ +/* */ +/* 14 03716 1777@ TAD OFFPAG */ +/* */ +/* Undefined symbols are marked in the symbol table listing by prepending */ +/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ +/* listing by prepending a '#' to the symbol. Examples are: */ +/* */ +/* #REDEF 04567 */ +/* SWITCH 07612 */ +/* ?UNDEF 00000 */ +/* */ +/* Refer to the code for the diagnostic messages generated. */ +/* */ +/* REFERENCES: */ +/* This assembler is based on the pal assember by: */ +/* Douglas Jones and */ +/* Rich Coon */ +/* */ +/* COPYRIGHT NOTICE: */ +/* This is free software. There is no fee for using it. You may make */ +/* any changes that you wish and also give it away. If you can make */ +/* a commercial product out of it, fine, but do not put any limits on */ +/* the purchaser's right to do the same. If you improve it or fix any */ +/* bugs, it would be nice if you told me and offered me a copy of the */ +/* new version. */ +/* */ +/* */ +/* Amendments Record: */ +/* Version Date by Comments */ +/* ------- ------- --- --------------------------------------------------- */ +/* v1.0 12Apr96 GAM Original */ +/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ +/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ +/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ +/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ +/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ +/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ +/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ +/* v3.0 14Feb97 RMS MACRO8X features. */ +/* */ +/******************************************************************************/ + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 3 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 /* Must be < 26 */ +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ +#define TITLELEN 63 +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 0017777 +#define LIT_BASE 0017400 +#define INDIRECT_BIT 0020000 +#define LAST_PAGE_LOC 0017777 +#define OP_CODE 0740000 + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +/* Macro to get the address plus one of the end of an array. */ +#define BEYOND(a) ((a) + DIM(A)) + +#define is_blank(c) ((c==' ') || (c=='\f') || (c=='>')) +#define isend(c) ((c=='\0')|| (c=='\n')) +#define isdone(c) ((c=='/') || (isend(c)) || (c=='\t')) + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) +#define M_DEFINED(s) ((s & DEFINED) == DEFINED) +#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) ((s & FIXED) == FIXED) +#define M_LABEL(s) ((s & LABEL) == LABEL) +#define M_MRI(s) ((s & MRI) == MRI) +#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) +#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) +#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) +#define M_MACRO(s) ((s & MACRO) == MACRO) +#define M_UNDEFINED(s) (!M_DEFINED(s)) +#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) + +/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ +#define M_DEF(s) (M_DEFINED(s)) +#define M_COND(s) (M_DEFINED(s)) +#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +/* */ +/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ +/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ +/* defined or undefined. The condition bit is set when the symbol is defined */ +/* during pass 1 and reset on pass 2 at the location the symbol was defined */ +/* during pass 1. When processing conditionals during pass 2, if the symbol */ +/* is defined and the condition bit is set, the symbol is treated as if it */ +/* were undefined. This gives consistent behavior of the conditional */ +/* pseudo-ops during both pass 1 and pass 2. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + MRI = 0004 | DEFINED, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + CONDITION = 0200 | DEFINED, + MACRO = 0400 | DEFINED, + MRIFIX = MRI | FIXED | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t +{ + DECIMAL, DEFINE, EJECT, IFDEF, IFNDEF, IFNZERO, IFZERO, + LIST, NOLIST, OCTAL, START, TEXT, TITLE, VFD +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct lpool_t +{ + WORD32 error; /* True if error message has been printed. */ + WORD32 pool[LIT_BASE]; +}; +typedef struct lpool_t LPOOL_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int copyMacLine( int length, int from, int term, int nargs ); +int compareSymbols( const void *a, const void *b ); +void conditionFalse( void ); +void conditionTrue( void ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void endOfBinary( void ); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T *eval( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +SYM_T *getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name ); +void moveToEndOfLine( void ); +void nextLexBlank( void ); +void nextLexeme( void ); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudoOperators( PSEUDO_T val ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchObject( WORD32 val ); +void punchTriplet( WORD32 val ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +BOOL testForLiteralCollision( WORD32 loc ); +void topOfForm( char *title, char *sub_title ); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup and when the EXPUNGE pseudo-op is executed. */ +SYM_T pseudo[] = +{ + { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ + { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ + { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ + { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ + { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ + { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ + { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ + { PSEUDO, "LIST", LIST }, /* Enable listing. */ + { PSEUDO, "NOLIST", NOLIST }, /* Disable listing. */ + { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ + { PSEUDO, "START", START }, /* Set starting address. */ + { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ + { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ + { PSEUDO, "VFD", VFD }, /* Variable field definition. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +#define DAC 0040000 +#define JMP 0600000 +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { MRIFIX, "CAL", 0000000 }, + { MRIFIX, "DAC", 0040000 }, + { MRIFIX, "JMS", 0100000 }, + { MRIFIX, "DZM", 0140000 }, + { MRIFIX, "LAC", 0200000 }, + { MRIFIX, "XOR", 0240000 }, + { MRIFIX, "ADD", 0300000 }, + { MRIFIX, "TAD", 0340000 }, + { MRIFIX, "XCT", 0400000 }, + { MRIFIX, "ISZ", 0440000 }, + { MRIFIX, "AND", 0500000 }, + { MRIFIX, "SAD", 0540000 }, + { MRIFIX, "JMP", 0600000 }, + { MRIFIX, "I", 0020000 }, + { DEFFIX, "EAE", 0640000 }, + { DEFFIX, "IOT", 0700000 }, + { DEFFIX, "OPR", 0740000 }, + { DEFFIX, "LAW", 0760000 }, + { DEFFIX, "LAM", 0777777 }, + /* EAE Microinstructions */ + { DEFFIX, "OSC", 0640001 }, + { DEFFIX, "LACS", 0641001 }, + { DEFFIX, "OMQ", 0640002 }, + { DEFFIX, "LACQ", 0641002 }, + { DEFFIX, "CMQ", 0640004 }, + { DEFFIX, "CLQ", 0650000 }, + { DEFFIX, "LMQ", 0652000 }, + { DEFFIX, "ABS", 0644000 }, + { DEFFIX, "GSM", 0664000 }, + { DEFFIX, "MUL", 0653122 }, + { DEFFIX, "MULS", 0657122 }, + { DEFFIX, "DIV", 0640323 }, + { DEFFIX, "DIVS", 0644323 }, + { DEFFIX, "IDIV", 0653323 }, + { DEFFIX, "IDIVS", 0657323 }, + { DEFFIX, "FRDIV", 0650323 }, + { DEFFIX, "FRDIVS", 0654323 }, + { DEFFIX, "NORM", 0640444 }, + { DEFFIX, "NORMS", 0660444 }, + { DEFFIX, "LRS", 0640500 }, + { DEFFIX, "LRSS", 0660500 }, + { DEFFIX, "LLS", 0640600 }, + { DEFFIX, "LLSS", 0660600 }, + { DEFFIX, "ALS", 0640700 }, + { DEFFIX, "ALSS", 0660700 }, + /* Operate Microinstructions */ + { DEFFIX, "NOP", 0740000 }, + { DEFFIX, "CMA", 0740001 }, + { DEFFIX, "CML", 0740002 }, + { DEFFIX, "OAS", 0740004 }, + { DEFFIX, "LAS", 0750004 }, + { DEFFIX, "RAL", 0740010 }, + { DEFFIX, "RCL", 0744010 }, + { DEFFIX, "RTL", 0742010 }, + { DEFFIX, "RAR", 0740020 }, + { DEFFIX, "RCR", 0744020 }, + { DEFFIX, "RTR", 0742020 }, + { DEFFIX, "HLT", 0740040 }, + { DEFFIX, "XX", 0740040 }, + { DEFFIX, "SMA", 0740100 }, + { DEFFIX, "SZA", 0740200 }, + { DEFFIX, "SNL", 0740400 }, + { DEFFIX, "SKP", 0741000 }, + { DEFFIX, "SPA", 0741100 }, + { DEFFIX, "SNA", 0741200 }, + { DEFFIX, "SZL", 0741400 }, + { DEFFIX, "CLL", 0744000 }, + { DEFFIX, "STL", 0744002 }, + { DEFFIX, "CLA", 0750000 }, + { DEFFIX, "CLC", 0750001 }, + { DEFFIX, "GLK", 0750010 }, + /* CPU IOT's */ + { DEFFIX, "CLSF", 0700001 }, + { DEFFIX, "IOF", 0700002 }, + { DEFFIX, "ION", 0700042 }, + { DEFFIX, "ITON", 0700062 }, + { DEFFIX, "CLOF", 0700004 }, + { DEFFIX, "CLON", 0700044 }, + { DEFFIX, "TTS", 0703301 }, + { DEFFIX, "SKP7", 0703341 }, + { DEFFIX, "CAF", 0703302 }, + { DEFFIX, "SEM", 0707701 }, + { DEFFIX, "EEM", 0707702 }, + { DEFFIX, "EMIR", 0707742 }, + { DEFFIX, "LEM", 0707704 }, + /* High Speed Paper Tape Reader */ + { DEFFIX, "RSF", 0700101 }, + { DEFFIX, "RRB", 0700112 }, + { DEFFIX, "RCF", 0700102 }, + { DEFFIX, "RSA", 0700104 }, + { DEFFIX, "RSB", 0700144 }, + /* High Speed Paper Tape Punch */ + { DEFFIX, "PSF", 0700201 }, + { DEFFIX, "PCF", 0700202 }, + { DEFFIX, "PSA", 0700204 }, + { DEFFIX, "PLS", 0700204 }, + { DEFFIX, "PSB", 0700244 }, + /* Keyboard */ + { DEFFIX, "KSF", 0700301 }, + { DEFFIX, "KRB", 0700312 }, + { DEFFIX, "IORS", 0700314 }, + /* Teleprinter */ + { DEFFIX, "TSF", 0700401 }, + { DEFFIX, "TCF", 0700402 }, + { DEFFIX, "TLS", 0700406 }, + /* Line Printer */ + { DEFINED, "LPSF", 0706501 }, + { DEFINED, "LPCB", 0706502 }, + { DEFINED, "LPB1", 0706566 }, + { DEFINED, "LPB2", 0706526 }, + { DEFINED, "LPB3", 0706546 }, + { DEFINED, "LPSE", 0706601 }, + { DEFINED, "LPCF", 0706602 }, + { DEFINED, "LPPB", 0706606 }, + { DEFINED, "LPLS", 0706626 }, + { DEFINED, "LPPS", 0706646 }, + /* Card Reader */ + { DEFFIX, "CRSF", 0706701 }, + { DEFFIX, "CRRB", 0706712 }, + { DEFFIX, "CRSA", 0706704 }, + { DEFFIX, "CRSB", 0706744 }, + /* DECtape */ + { DEFFIX, "MMDF", 0707501 }, + { DEFFIX, "MMEF", 0707541 }, + { DEFFIX, "MMRD", 0707512 }, + { DEFFIX, "MMWR", 0707504 }, + { DEFFIX, "MMBF", 0707601 }, + { DEFFIX, "MMRS", 0707612 }, + { DEFFIX, "MMLC", 0707604 }, + { DEFFIX, "MMSE", 0707644 }, +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ +int number_of_fixed_symbols; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +LPOOL_T cp; /* Storage for current page constants. */ + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T literal_gen_off = { "lit generation off", + "literal generation disabled" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char errorpathname[NAMELEN]; +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +char mac_buffer[MAC_MAX_LENGTH + 1]; +char *mac_bodies[MAC_TABLE_LENGTH]; +char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; +int mac_arg_pos[26] = { 0 }; + +int list_lineno; +int list_pageno; +char list_title[4*LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[4*LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +char mac_line[4*LINELEN]; /* Saved macro invocation line. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 checksum; /* Generated checksum */ +BOOL binary_data_output; /* Set true when data has been output. */ +WORD32 clc; /* Location counter */ +char delimiter; /* Character immediately after eval'd term. */ +BOOL end_of_input; /* End of all input files. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +BOOL indirect_generated; /* TRUE if an off page address generated. */ +WORD32 lexstartprev; /* Where previous lexeme started. */ +WORD32 lextermprev; /* Where previous lexeme ended. */ +WORD32 lexstart; /* Index of current lexeme on line. */ +WORD32 lexterm; /* Index of character after current lexeme. */ +WORD32 lit_loc; /* Base of literal pool. */ +WORD32 mac_cc; /* Saved cc after macro invocation. */ +WORD32 mac_count; /* Total macros defined. */ +char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ +WORD32 maxcc; /* Current line length. */ +BOOL nomac_exp; /* No macro expansions. */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +BOOL rim_mode; /* RIM mode output. */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +WORD32 start_addr; /* Saved start address. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ +SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + + +/******************************************************************************/ +/* */ +/* Function: main */ +/* */ +/* Synopsis: Starting point. Controls order of assembly. */ +/* */ +/******************************************************************************/ +int main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + binary_data_output = FALSE; + print_permanent_symbols = FALSE; + nomac_exp = TRUE; + rim_mode = TRUE; + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) + { + mac_bodies[ix] = NULL; + } + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Setup the error file in case symbol table overflows while installing the */ + /* permanent symbols. */ + errorfile = fopen( errorpathname, "w" ); + errors = 0; + save_error_count = 0; + pass = 0; /* This is required for symbol table initialization. */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) + { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type, 0 ); + } + + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Do pass one of the assembly */ + checksum = 0; + pass = 1; + onePass(); + errors_pass_1 = errors; + + /* Set up for pass two */ + errorfile = fopen( errorpathname, "w" ); + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + punchLeader( 0 ); + checksum = 0; + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) + { + /* Get the amount of space that will be required for the concordance. */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + + } + /* Allocate the necessary space. */ + xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); + + /* Clear the cross reference space. */ + for( ix = 0; ix < space; ix++ ) + { + xreftab[ix] = 0; + } + } + pass = 2; + onePass(); + + /* Undo effects of NOPUNCH for any following checksum */ + objectfile = objectsave; + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) + { + fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); + } + else + { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( stderr, " %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + { + printSymbolTable(); + } + + if( print_permanent_symbols ) + { + printPermanentSymbolTable(); + } + + if( xref ) + { + printCrossReference(); + } + + fclose( objectfile ); + fclose( listfile ); + fclose( errorfile ); + if( errors == 0 && errors_pass_1 == 0 ) + { + remove( errorpathname ); + } + + return( errors != 0 ); +} /* main() */ + +/******************************************************************************/ +/* */ +/* Function: getArgs */ +/* */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +/* */ +/******************************************************************************/ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + errorfile = NULL; + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ix++ ) + { + if( argv[ix][0] == '-' ) + { + for( jx = 1; argv[ix][jx] != 0; jx++ ) + { + switch( argv[ix][jx] ) + { + case 'd': + symtab_print = TRUE; + break; + +/* case 'r': + rim_mode = TRUE; + break; +*/ + case 'm': + nomac_exp = FALSE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- output macro expansions\n" ); +/* fprintf( stderr, " -r -- output rim format file\n" ); */ + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( errorpathname, pathname, jx ); + errorpathname[jx] = '\0'; + strcat( errorpathname, ".err" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + { + pathname[1] = '\\'; /* MS-DOS style pathname */ + } + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + strcpy( filename, &pathname[jx + 1] ); + +} /* getArgs() */ + + +/******************************************************************************/ +/* */ +/* Function: onePass */ +/* */ +/* Synopsis: Do one assembly pass. */ +/* */ +/******************************************************************************/ +void onePass() +{ + BOOL blanks; + int ix; + int jx; + char name[SYMLEN]; + WORD32 newclc; + BOOL scanning_line; + WORD32 start; + SYM_T *sym; + WORD32 term; + WORD32 val; + + clc = 0100; /* Default starting address is 100 octal. */ + start_addr = 0100; /* No starting address. */ + lit_loc = LIT_BASE; /* Literal pool base. */ + mac_count = 0; /* No macros defined. */ + mac_ptr = NULL; /* Not in a macro. */ + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) + { + if ( mac_bodies[ix] ) + { + free( mac_bodies[ix] ); + } + } + cp.error = FALSE; + listed = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + end_of_input = FALSE; + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + + while( TRUE ) + { + readLine(); + nextLexeme(); + + scanning_line = TRUE; + while( scanning_line ) + { + if( end_of_input ) + { + endOfBinary(); + fclose( infile ); + return; + } + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case '\t': + nextLexeme(); + break; + + default: + for( jx = lexstart; jx < maxcc; jx++ ) + { + if( is_blank( line[jx] ) || isdone( line[jx] )) break; + } + if( line[jx] == '/') + { + newclc = (getExpr())->val & 077777; + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + { + clc = newclc; + } + printLine( line, 0, newclc, LINE_VAL ); + cc = jx + 1; + nextLexeme(); + while( line[lexstart] == '\t' ) nextLexeme(); + break; + } + + switch( line[lexterm] ) + { + case ',': + if( isalpha( line[lexstart] )) + { + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup( lexemeToName( name, lexstart, lexterm )); + if( M_DEFINED( sym->type )) + { + if( sym->val != clc && pass == 2 ) + { + errorSymbol( &duplicate_label, sym->name, lexstart ); + } + sym->type = sym->type | DUPLICATE; + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, clc, LABEL ); + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + nextLexeme(); /* skip label */ + nextLexeme(); /* skip comma */ + while( line[lexstart] == '\t' ) nextLexeme(); + break; + + case '=': + if( isalpha( line[lexstart] )) + { + start = lexstart; + term = lexterm; + delimiter = line[lexterm]; + nextLexBlank(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else + { + errorLexeme( &symbol_syntax, lexstartprev ); + nextLexeme(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + getExprs(); /* skip expression */ + } + while( line[lexstart] == '\t' ) nextLexeme(); + break; + + default: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + val = sym->val; + if( M_MACRO( sym->type )) + { /* Find arguments. */ + blanks = TRUE; /* Expecting blanks. */ + for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) + { + if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; + else if( blanks ) + { + mac_arg_pos[jx++] = cc; + blanks = FALSE; + } + } /* end for */ + for( ; jx < MAC_MAX_ARGS; jx++ ) + { + mac_arg_pos[jx] = 0; + } + for( jx = 0; jx < LINELEN; jx++ ) + { + mac_line[jx] = line[jx]; + } + mac_cc = cc; /* Save line and position in line. */ + mac_ptr = mac_bodies[val]; + if( mac_ptr ) scanning_line = FALSE; + else nextLexeme(); + } /* end if macro */ + else if( M_PSEUDO( sym->type )) + { + nextLexeme(); /* Skip symbol */ + scanning_line = pseudoOperators( (PSEUDO_T)val & 0777777 ); + } + else + { + /* Identifier is not a pseudo-op, interpret as load value */ + punchOutObject( clc, getExprs() & 0777777 ); + incrementClc(); + } + } + else + { + /* Identifier is a value, interpret as load value */ + punchOutObject( clc, getExprs() & 0777777 ); + incrementClc(); + } + break; + } /* end switch */ + break; + } /* end switch */ + } /* end if */ + } /* end while( scanning_line ) */ + } /* end while( TRUE ) */ +} /* onePass() */ + + +/******************************************************************************/ +/* */ +/* Function: getExprs */ +/* */ +/* Synopsis: Or together a list of blank separated expressions, from the */ +/* current lexeme onward. Leave the current lexeme as */ +/* the last one in the list. */ +/* */ +/******************************************************************************/ +WORD32 getExprs() +{ + SYM_T *symv; + SYM_T *symt; + WORD32 temp; + SYMTYP temp_type; + WORD32 value; + SYMTYP value_type; + + symv = getExpr(); + value = symv->val; + value_type = symv->type; + + while( TRUE ) + { + if( isdone( line[lexstart] ) || line[lexstart] == ')' ) + { + return( value ); + } + + /* Interpret space as add */ + symt = getExpr(); + temp = symt->val & 0777777; + temp_type = symt->type; + + switch( value_type ) + { + case MRI: + case MRIFIX: + /* Previous symbol was a Memory Reference Instruction. */ + switch( temp_type ) + { + case MRI: + case MRIFIX: + /* Current symbol is also a Memory Reference Instruction. */ + value |= temp; /* Just OR the MRI instructions. */ + break; + + default: + /* Now have the address part of the MRI instruction. */ + if(( clc & 060000) == ( temp & 060000)) + { + value += ( temp & ADDRESS_FIELD ); /* In range MRI. */ + } + else + { + if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) + { + /* Already indirect, can't generate */ + errorSymbol( &illegal_indirect, symt->name, lexstartprev ); + } + else + { + /* Now fix off page reference. */ + /* Search current page literal pool for needed value. */ + /* Set Indirect */ + value += ( INDIRECT_BIT | insertLiteral( &cp, clc, temp & 077777)); + indirect_generated = TRUE; + } + } + break; + } + break; + + default: + value = value + temp; /* Normal 18 bit value. */ + if( value >= 0777777 ) value = ( value + 1 ) & 0777777; + break; + } + } /* end while */ +} /* getExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getExpr */ +/* */ +/* Synopsis: Get an expression, from the current lexeme onward, leave the */ +/* current lexeme as the one after the expression. Expressions */ +/* contain terminal symbols (identifiers) separated by operators. */ +/* */ +/******************************************************************************/ +SYM_T *getExpr() +{ + delimiter = line[lexterm]; + + if( line[lexstart] == '-' ) + { + nextLexBlank(); + sym_getexpr = *(eval()); + sym_getexpr.val = sym_getexpr.val ^ 0777777; + } + else + { + sym_getexpr = *(eval()); + } + + + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val += (eval())->val; + if( sym_getexpr.val >= 01000000 ) + { + sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; + } + break; + + case '-': /* subtract */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val = sym_getexpr.val + + ( (eval())->val ^ 0777777 ); + if( sym_getexpr.val >= 01000000 ) + { + sym_getexpr.val = ( sym_getexpr.val + 1 ) & 0777777; + } + break; + + case '^': /* multiply */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val *= (eval())->val; + break; + + case '%': /* divide */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val /= (eval())->val; + break; + + case '&': /* and */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val &= (eval())->val; + break; + + case '!': /* or */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val |= (eval())->val; + break; + + default: + if( isend( line[lexstart] )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '/': + case '\t': + case ')': + case '<': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + } + return( &sym_getexpr ); + } + } /* end while */ +} /* getExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: eval */ +/* */ +/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ +/* */ +/******************************************************************************/ +SYM_T *eval() +{ + WORD32 digit; + WORD32 from; + WORD32 loc; + SYM_T *sym; + WORD32 val; + + val = 0; + + delimiter = line[lexterm]; + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + if( M_UNDEFINED( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &undefined_symbol, sym->name, lexstart ); + } + nextLexeme(); + return( sym ); + } + else if( M_PSEUDO( sym->type )) + { + if( sym->val == DECIMAL ) + { + radix = 10; + } + else if( sym->val == OCTAL ) + { + radix = 8; + } + else if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else + { + nextLexeme(); + return( sym ); + } + } + else if( isdigit( line[lexstart] )) + { + from = lexstart; + val = 0; + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + digit = (WORD32) line[from++] - (WORD32) '0'; + if( digit < radix ) + { + val = val * radix + digit; + } + else + { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + from = lexterm; + } + } + else + { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + from = lexterm; + } + } + nextLexeme(); + sym_eval.val = val; + return( &sym_eval ); + } + else + { + switch( line[lexstart] ) + { + case '"': /* Character literal */ + if( cc + 2 < maxcc ) + { + val = line[lexstart + 1] | 0200; + delimiter = line[lexstart + 2]; + cc = lexstart + 2; + } + else + { + errorMessage( &no_literal_value, lexstart ); + } + nextLexeme(); + break; + + case '.': /* Value of Current Location Counter */ + val = clc; + nextLexeme(); + break; + + case '(': /* Generate literal on current page. */ + nextLexBlank(); /* Skip paren */ + val = getExprs() & 0777777; + + if( line[lexstart] == ')' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end paren */ + } + else + { + /* errorMessage( "parens", NULL ); */ + } + + loc = insertLiteral( &cp, clc, val ); + sym_eval.val = loc + ( clc & 060000 ); + return( &sym_eval ); + + default: + switch( line[lexstart] ) + { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + + default: + errorMessage( &illegal_character, lexstart ); + break; + } + val = 0; /* On error, set value to zero. */ + nextLexBlank(); /* Go past illegal character. */ + } + } + sym_eval.val = val; + return( &sym_eval ); +} /* eval() */ + +/******************************************************************************/ +/* */ +/* Function: incrementClc */ +/* */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +/* */ +/******************************************************************************/ +WORD32 incrementClc() +{ + testForLiteralCollision( clc ); + clc = (( clc + 1 ) & ADDRESS_FIELD ); + return( clc ); +} /* incrementClc() */ + + +/******************************************************************************/ +/* */ +/* Function: testForLiteralCollision */ +/* */ +/* Synopsis: Test the given location for collision with the literal tables. */ +/* */ +/******************************************************************************/ +BOOL testForLiteralCollision( WORD32 loc ) +{ + WORD32 pagelc; + BOOL result = FALSE; + + pagelc = loc & ADDRESS_FIELD; + if( ( pagelc >= lit_loc ) && ( lit_loc != LIT_BASE ) && !cp.error ) + { + errorMessage( &literal_overflow, -1 ); + cp.error = TRUE; + result = TRUE; + } + return( result ); +} /* testForLiteralCollision() */ + + +/******************************************************************************/ +/* */ +/* Function: readLine */ +/* */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +/* */ +/******************************************************************************/ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char mc; + char inpline[4*LINELEN]; + + listLine(); /* List previous line if needed. */ + indirect_generated = FALSE; /* Mark no indirect address generated. */ + error_in_line = FALSE; /* No error in line. */ + + if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ + { + mac_ptr = NULL; + for( ix = 0; ix < LINELEN; ix++ ) + { + line[ix] = mac_line[ix]; /* Restore invoking line. */ + } + cc = lexstartprev = mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + listed = TRUE; /* Already listed. */ + return; + } + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( mac_ptr ) /* Inside macro? */ + { + maxcc = 0; + do + { + mc = *mac_ptr++; /* Next character. */ + if( islower( mc )) /* Encoded argument number? */ + { + ix = mc - 'a'; /* Convert to index. */ + if( iy = mac_arg_pos[ix] ) + { + do /* Copy argument string. */ + { + line[maxcc++] = mac_line[iy++]; + } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && + ( !isdone( mac_line[iy] ))); + } + } + else /* Ordinary character, just copy. */ + { + line[maxcc++] = mc; + } + } while( !isend( mc )); + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ +READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) + { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) /* More files? */ + { + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + goto READ_LINE; + } + else + { + end_of_input = TRUE; + } + } + + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) + { + if( inpline[ix] == '\f' ) + { + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + } + else + { + line[iy++] = inpline[ix]; + } + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) + { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine() */ + + +/******************************************************************************/ +/* */ +/* Function: listLine */ +/* */ +/* Synopsis: Output a line to the listing file. */ +/* */ +/******************************************************************************/ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printPageBreak */ +/* */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +/* */ +/******************************************************************************/ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + if( !list_title_set ) + { + strcpy( list_title, line ); + if( list_title[strlen(list_title) - 1] == '\n' ) + { + list_title[strlen(list_title) - 1] = '\0'; + } + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + list_title_set = TRUE; + } + topOfForm( list_title, NULL ); + + } +} /* printPageBreak() */ + + +/******************************************************************************/ +/* */ +/* Function: printLine */ +/* */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +/* */ +/******************************************************************************/ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %6.6o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %6.6o\n", val ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + if( indirect_generated ) + { + fprintf( listfile, "%5d %5.5o %6.6o@ ", lineno, loc, val ); + } + else + { + fprintf( listfile, "%5d %5.5o %6.6o ", lineno, loc, val ); + } + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %6.6o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printErrorMessages */ +/* */ +/* Synopsis: Output any error messages from the current list of errors. */ +/* */ +/******************************************************************************/ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s ", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages() */ + + +/******************************************************************************/ +/* */ +/* Function: endOfBinary */ +/* */ +/* Synopsis: Outputs both literal tables at the end of a binary segment. */ +/* */ +/******************************************************************************/ +void endOfBinary() +{ + punchLiteralPool( &cp, clc - 1 ); + if( start_addr >= 0) + { + punchTriplet( JMP | ( start_addr & 017777 )); + punchTriplet( 0 ); + } + return; +} /* endOfBinary() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLeader */ +/* */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +/* */ +/******************************************************************************/ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0, objectfile ); + } + } +} /* punchLeader() */ + + +/******************************************************************************/ +/* */ +/* Function: punchObject */ +/* */ +/* Synopsis: Put one character to object file and include it in checksum. */ +/* */ +/******************************************************************************/ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + { + fputc( val, objectfile ); + } + checksum += val; + binary_data_output = TRUE; +} /* punchObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOutObject */ +/* */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +/* */ +/******************************************************************************/ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, loc, val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLocObject */ +/* */ +/* Synopsis: Output the word (with origin if rim format) to the object file.*/ +/* */ +/******************************************************************************/ +void punchLocObject( WORD32 loc, WORD32 val ) +{ + punchTriplet( DAC | loc ); + punchTriplet( val ); +} /* punchLocObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchTriplet */ +/* */ +/* Synopsis: Output 18b word as three 6b characters with ho bit set. */ +/* */ +/******************************************************************************/ +void punchTriplet( WORD32 val ) +{ + punchObject((( val >> 12) & 077) | 0200 ); + punchObject((( val >> 6 ) & 077) | 0200 ); + punchObject(( val & 077) | 0200 ); +} /* punchTriplet */ + + +/******************************************************************************/ +/* */ +/* Function: punchLiteralPool */ +/* */ +/* Synopsis: Output the current page data. */ +/* */ +/******************************************************************************/ +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) +{ + WORD32 loc; + WORD32 tmplc; + + if( lit_loc < LIT_BASE ) + { + for( loc = lit_loc; loc < LIT_BASE; loc++ ) + { + tmplc = loc + ( lpool_page & 060000 ); + printLine( line, tmplc, p->pool[loc], LOC_VAL ); + punchLocObject( tmplc, p->pool[loc] ); + } + p->error = FALSE; + lit_loc = LIT_BASE; + } +} /* punchLiteralPool() */ + + +/******************************************************************************/ +/* */ +/* Function: insertLiteral */ +/* */ +/* Synopsis: Add a value to the given literal pool if not already in pool. */ +/* Return the location of the value in the pool. */ +/* */ +/******************************************************************************/ +WORD32 insertLiteral( LPOOL_T *p, WORD32 pool_page, WORD32 value ) +{ + WORD32 ix; + + /* Search the literal pool for any occurence of the needed value. */ + ix = LIT_BASE - 1; + while( ix >= lit_loc && p->pool[ix] != value ) + { + ix--; + } + + /* Check if value found in literal pool. If not, then insert value. */ + if( ix < lit_loc ) + { + lit_loc--; + p->pool[lit_loc] = value; + ix = lit_loc; + } + return( ix ); +} /* insertLiteral() */ + + +/******************************************************************************/ +/* */ +/* Function: printSymbolTable */ +/* */ +/* Synopsis: Output the symbol table. */ +/* */ +/******************************************************************************/ +void printSymbolTable() +{ + int col; + int cx; + char *fmt; + int ix; + char mark; + int page; + int row; + int symbol_base; + int symbol_lines; + + symbol_base = number_of_fixed_symbols; + + for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) + { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE - page_lineno; + + for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) + { + list_lineno++; + page_lineno++; + fprintf( listfile, "%5d", list_lineno ); + + for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) + { + /* Get index of symbol for the current line and column */ + cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; + cx += symbol_base; + + /* Make sure that there is a symbol to be printed. */ + if( number_of_fixed_symbols <= cx && cx < symbol_top ) + { + switch( symtab[cx].type & LABEL ) + { + case LABEL: + fmt = " %c%-6.6s %5.5o "; + break; + + default: + fmt = " %c%-6.6s %4.4o "; + break; + } + + switch( symtab[cx].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); + ix++; + } + } + fprintf( listfile, "\n" ); + } + } +} /* printSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printPermanentSymbolTable */ +/* */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +/* */ +/******************************************************************************/ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + char *s_type; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " EXPUNGE\n/\n" ); + /* Print the memory reference instructions first. */ + s_type = "FIXMRI"; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_MRI( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_FIXED( symtab[ix].type )) + { + if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + } + fprintf( permfile, "/\n FIXTAB\n" ); + fclose( permfile ); +} /* printPermanentSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printCrossReference */ +/* */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +/* */ +/******************************************************************************/ +void printCrossReference() +{ + int ix; + int symbol_base; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + symbol_base = number_of_fixed_symbols; + + for( ix = symbol_base; ix < symbol_top; ix++ ) + { + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref ); + } + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol. */ + xc_refcount = symtab[ix].xref_count; + xc_index = symtab[ix].xref_index; + /* Determine how to label symbol on concordance. */ + switch( symtab[ix].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", symtab[ix].name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) + { + if( xc_cols >= XREF_COLUMNS ) + { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref); + } + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } +} /* printCrossReference() */ + + +/******************************************************************************/ +/* */ +/* Function: topOfForm */ +/* */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +/* */ +/******************************************************************************/ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + { + fprintf( listfile, "\f" ); + } + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm() */ + + +/******************************************************************************/ +/* */ +/* Function: lexemeToName */ +/* */ +/* Synopsis: Convert the current lexeme into a string. */ +/* */ +/******************************************************************************/ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + WORD32 to; + + to = 0; + + while( from < term && to < ( SYMLEN - 1 )) + { + name[to++] = toupper( line[from++] ); + } + + while( to < SYMLEN ) + { + name[to++] = '\0'; + } + return( name ); +} /* lexemeToName() */ + +/******************************************************************************/ +/* */ +/* Function: defineLexeme */ +/* */ +/* Synopsis: Put lexeme into symbol table with a value. */ +/* */ +/******************************************************************************/ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: defineSymbol */ +/* */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +/* */ +/******************************************************************************/ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + } + return ( sym ); + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + + /* Now set the value and the type. */ + sym->val = ( type == LABEL) ? val : val & 0777777; + sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; + return( sym ); +} /* defineSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: lookup */ +/* */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +/* */ +/******************************************************************************/ +SYM_T *lookup( char *name ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + + /* First search the permanent symbols. */ + lx = 0; + ix = binarySearch( name, lx, number_of_fixed_symbols ); + + /* If symbol not in permanent symbol table. */ + if( ix < 0 ) + { + /* Now try the user symbol table. */ + ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); + + /* If symbol not in user symbol table. */ + if( ix < 0 ) + { + /* Must put symbol in table if index is negative. */ + ix = ~ix; + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) + { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + { + symtab[rx + 1] = symtab[rx]; + } + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + strcpy( symtab[ix].name, name ); + symtab[ix].type = UNDEFINED; + symtab[ix].val = 0; + symtab[ix].xref_count = 0; + if( xref && pass == 2 ) + { + xreftab[symtab[ix].xref_index] = 0; + } + } + } + + return( &symtab[ix] ); /* Return the location of the symbol. */ +} /* lookup() */ + + +/******************************************************************************/ +/* */ +/* Function: binarySearch */ +/* */ +/* Synopsis: Searches the symbol table within the limits given. If the */ +/* symbol is not in the table, it returns the insertion point. */ +/* */ +/******************************************************************************/ +int binarySearch( char *name, int start, int symbol_count ) +{ + int lx; /* Left index */ + int mx; /* Middle index */ + int rx; /* Right index */ + int compare; /* Results of comparison */ + + lx = start; + rx = symbol_count - 1; + while( lx <= rx ) + { + mx = ( lx + rx ) / 2; /* Find center of search area. */ + + compare = strcmp( name, symtab[mx].name ); + + if( compare < 0 ) + { + rx = mx - 1; + } + else if( compare > 0 ) + { + lx = mx + 1; + } + else + { + return( mx ); /* Found a match in symbol table. */ + } + } /* end while */ + return( ~lx ); /* Return insertion point. */ +} /* binarySearch() */ + + +/******************************************************************************/ +/* */ +/* Function: compareSymbols */ +/* */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +/* */ +/******************************************************************************/ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols() */ + +/******************************************************************************/ +/* */ +/* Function: copyMacLine */ +/* */ +/* Synopsis: Used to copy a macro line to the macro buffer. */ +/* */ +/******************************************************************************/ +int copyMacLine( int length, int from, int term, int nargs ) +{ + char name[SYMLEN]; + int ix; + int jx; + int kx; + BOOL bl; + + bl = TRUE; + for( ix = from; ix < term; ix++ ) + { + if( !is_blank( line[ix] ) || ( line[ix] == '\t' )) bl = FALSE; + } + if( bl || ( length < 0 )) return length; + if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; + for( ix = from; ix < term; ) + { + if( nargs && isalpha( line[ix] )) /* Start of symbol? */ + { + for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ + { + if( !isalnum( line[jx] )) break; + } + lexemeToName( name, ix, jx ); /* Make into name. */ + for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ + { + if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) + { + mac_buffer[length++] = 'a' + (char) kx; + for( ix++; ix < jx; ix++ ) + { + mac_buffer[length++] = 'z'; + } + break; + } /* end if strncmp */ + } /* end for kx */ + if( kx >= nargs ) + { + for ( ; ix < jx; ) + { + mac_buffer[length++] = toupper( line[ix++] ); + } + } + } /*end if nargs */ + else + { + mac_buffer[length++] = toupper( line[ix++] ); + } /* end else */ + } /* end for ix */ + mac_buffer[length++] = '\n'; + mac_buffer[length] = 0; + return length; +} + +/******************************************************************************/ +/* */ +/* Function: evalSymbol */ +/* */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +/* */ +/******************************************************************************/ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm )); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 ) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: moveToEndOfLine */ +/* */ +/* Synopsis: Move the parser input to the end of the current input line. */ +/* */ +/******************************************************************************/ +void moveToEndOfLine() +{ + while( !isend( line[cc] )) cc++; + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine() */ + +/******************************************************************************/ +/* */ +/* Function: nextLexeme */ +/* */ +/* Synopsis: Get the next lexical element from input line. */ +/* */ +/******************************************************************************/ +void nextLexeme() +{ + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + while( is_blank( line[cc] ) || + (( line[cc] == '\t' ) && ( line[cc+1] == '\t' )) || + (( line[cc] == '\t' ) && isdone( line[cc+1] ))) { cc++; } + lexstart = cc; + + if( isalnum( line[cc] )) + { + while( isalnum( line[cc] )) { cc++; } + } + else if( isend( line[cc] )) + { + /* End-of-Line, don't advance cc! */ + } + else + { + switch( line[cc] ) + { + case '"': /* Quoted letter */ + if( cc + 2 < maxcc ) + { + cc++; + cc++; + } + else + { + errorMessage( &no_literal_value, lexstart ); + cc++; + } + break; + + case '/': /* Comment, don't advance cc! */ + break; + + default: /* All other punctuation. */ + cc++; + break; + } + } + lexterm = cc; +} /* nextLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: nextLexBlank */ +/* */ +/* Synopsis: Used to prevent illegal blanks in expressions. */ +/* */ +/******************************************************************************/ +void nextLexBlank() +{ + nextLexeme(); + if( is_blank( delimiter )) + { + errorMessage( &illegal_blank, lexstart - 1 ); + } + delimiter = line[lexterm]; +} /* nextLexBlank() */ + + + +/******************************************************************************/ +/* */ +/* Function: pseudoOperators */ +/* */ +/* Synopsis: Process pseudo-ops (directives). */ +/* */ +/******************************************************************************/ +BOOL pseudoOperators( PSEUDO_T val ) +{ + int count; + int delim; + int index; + int ix; + WORD32 length; + int level; + int lexstartsave; + int pack; + int pos; + int radixprev; + BOOL status; + SYM_T *sym; + WORD32 value; + WORD32 word; + int width; + static int mask_tab[19] = { 0000000, + 0000001, 0000003, 0000007, 0000017, 0000037, 0000077, + 0000177, 0000377, 0000777, 0001777, 0003777, 0007777, + 0017777, 0037777, 0077777, 0177777, 0377777, 0777777 }; + + status = TRUE; + switch( (PSEUDO_T) val ) + { + case DECIMAL: + radix = 10; + break; + + case DEFINE: + count = 0; + index = 0; + lexstartsave = lexstart; + while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && + ( count < MAC_MAX_ARGS )) + { + if ( !isalpha( line[lexstart] ) && ( index == 0 )) + { + index = lexstart; + } + lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); + nextLexeme(); + } + if( count == 0 ) /* No macro name. */ + { + errorMessage( &no_macro_name, lexstartsave ); + index = 1; + } + else if( index ) /* Bad argument name. */ + { + errorMessage( &bad_dummy_arg, index ); + } + else if( mac_count >= MAC_TABLE_LENGTH ) + { + errorMessage( ¯o_table_full, lexstartsave ); + index = 1; + } + else + { + value = mac_count; + mac_count++; /* Value is entry in mac_bodies. */ + defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); + } + if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) + { + readLine(); + nextLexeme(); + } + if( index ) + { + conditionFalse(); /* On error skip macro body. */ + } + else if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + index = lexstart + 1; + length = 0; + level = 1; + while( level > 0 ) + { + if( end_of_input ) + { + break; + } + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + length = copyMacLine( length, index, cc, count - 1 ); + readLine(); + index = 0; + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + length = copyMacLine( length, index, cc - 1, count - 1 ); + if( length < 0 ) + { + errorMessage (¯o_too_long, lexstart ); + } + else if( length == 0 ) + { + mac_bodies[value] = NULL; + } + else + { + mac_bodies[value] = (char *) malloc( length + 1 ); + if( mac_bodies[value] ) + { + strncpy( mac_bodies[value], mac_buffer, length ); + *( mac_bodies[value] + length ) = 0; + } + else + { + errorMessage( &no_virtual_memory, lexstart ); + } + } + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } /* end if */ + break; + + case EJECT: + page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ + status = FALSE; /* This will force reading of next line */ + break; + + case IFDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNZERO: + if( (getExpr())->val == 0 ) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + break; + + case IFZERO: + if( (getExpr())->val == 0 ) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + break; + + case LIST: + listfile = listsave; + break; + + case NOLIST: + listfile = NULL; + break; + + case OCTAL: + radix = 8; + break; + + case START: + if( !isdone( line[lexstart] )) + { + start_addr = (getExpr())->val & 077777; + nextLexeme(); + } + printLine( line, 0, start_addr, LINE_VAL ); + status = FALSE; + break; + + case TEXT: + delim = line[lexstart]; + pack = 0; + count = 0; + index = lexstart + 1; + while( line[index] != delim && !isend( line[index] )) + { + pack = ( pack << 6 ) | ( line[index] & 077 ); + count++; + if( count > 2 ) + { + punchOutObject( clc, pack ); + incrementClc(); + count = 0; + pack = 0; + } + index++; + } + + if( count == 2 ) + { + punchOutObject( clc, pack << 6 ); + } + else if (count == 1) + { + punchOutObject( clc, pack << 12 ); + } + else + { + punchOutObject( clc, 0 ); + } + incrementClc(); + + if( isend( line[index] )) + { + cc = index; + lexterm = cc; + errorMessage( &text_string, cc ); + } + else + { + cc = index + 1; + lexterm = cc; + } + nextLexeme(); + break; + + case TITLE: + delim = line[lexstart]; + ix = lexstart + 1; + /* Find string delimiter. */ + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( !isend( line[ix] ) ) + { + count = 0; + ix = lexstart + 1; + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + list_title[count] = line[ix]; + count++; + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + + cc = ix + 1; + lexterm = cc; + page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ + list_title_set = TRUE; + } + else + { + cc = ix; + lexterm = cc; + errorMessage( &text_string, cc ); + } + + nextLexeme(); + break; + + case VFD: + pos = 0; + word = 0; + radixprev = radix; + while( !isdone (line[lexstart] )) + { + lexstartsave = lexstart; + radix = 10; + width = (getExpr())->val; /* Get field width. */ + radix = radixprev; + if( (width <= 0) || ((width + pos) > 18) || (line[lexstart] != ':') ) + { + errorMessage( &illegal_vfd_value, lexstartsave ); + } + nextLexBlank(); /* Skip colon. */ + value = (getExpr())->val; /* Get field value. */ + if( line[lexterm] == ',' ) cc++; + nextLexeme(); /* Advance to next field. */ + pos = pos + width; + if( pos <= 18 ) + { + word = word | ((value & mask_tab[width]) << (18 - pos)); + } + } + punchOutObject( clc, word ); + incrementClc(); + break; + + default: + break; + } /* end switch for pseudo-ops */ + return( status ); +} /* pseudoOperators() */ + + +/******************************************************************************/ +/* */ +/* Function: conditionFalse */ +/* */ +/* Synopsis: Called when a false conditional has been evaluated. */ +/* Lex should be the opening <; ignore all text until */ +/* the closing >. */ +/* */ +/******************************************************************************/ +void conditionFalse() +{ + int level; + + if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + level = 1; + while( level > 0 ) + { + if( end_of_input ) + { + break; + } + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + readLine(); + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionFalse() */ + +/******************************************************************************/ +/* */ +/* Function: conditionTrue */ +/* */ +/* Synopsis: Called when a true conditional has been evaluated. */ +/* Lex should be the opening <; skip it and setup for */ +/* normal assembly. */ +/* */ +/******************************************************************************/ +void conditionTrue() +{ + if( line[lexstart] == '<' ) + { + nextLexeme(); /* Skip the opening '<' */ + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionTrue() */ + + +/******************************************************************************/ +/* */ +/* Function: errorLexeme */ +/* */ +/* Synopsis: Display an error message using the current lexical element. */ +/* */ +/******************************************************************************/ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: errorSymbol */ +/* */ +/* Synopsis: Display an error message with a given string. */ +/* */ +/******************************************************************************/ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: errorMessage */ +/* */ +/* Synopsis: Display an error message without a name argument. */ +/* */ +/******************************************************************************/ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage() */ + +/******************************************************************************/ +/* */ +/* Function: saveError */ +/* */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +/* */ +/******************************************************************************/ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + { + printErrorMessages(); + } +} /* saveError() */ +/* End-of-File */ diff --git a/crossassemblers/macro8x/macro8x.c b/crossassemblers/macro8x/macro8x.c index 69ac403..e277666 100644 --- a/crossassemblers/macro8x/macro8x.c +++ b/crossassemblers/macro8x/macro8x.c @@ -1,4251 +1,4251 @@ -/******************************************************************************/ -/* */ -/* Program: MACRO8X */ -/* File: macro8x.c */ -/* Author: Gary A. Messenbrink */ -/* MACRO8X modifications: Bob Supnik (:) : error: at Loc = */ -/* */ -/* An example error message is: */ -/* */ -/* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ -/* */ -/* The error diagnostics put in the listing start with a two character */ -/* error code (if appropriate) and a short message. A carat '^' is */ -/* placed under the item in error if appropriate. */ -/* An example error message is: */ -/* */ -/* 17 07616 3000 DCA UNDEF */ -/* UD undefined ^ */ -/* 18 07617 1777 TAD I DUMMY */ -/* */ -/* When an indirect is generated, an at character '@' is placed after the */ -/* the instruction value in the listing as an indicator as follows: */ -/* */ -/* 14 03716 1777@ TAD OFFPAG */ -/* */ -/* Undefined symbols are marked in the symbol table listing by prepending */ -/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ -/* listing by prepending a '#' to the symbol. Examples are: */ -/* */ -/* #REDEF 04567 */ -/* SWITCH 07612 */ -/* ?UNDEF 00000 */ -/* */ -/* Refer to the code for the diagnostic messages generated. */ -/* */ -/* BUGS */ -/* Only a minimal effort has been made to keep the listing format */ -/* anything like the PAL-8 listing format. */ -/* The operation of the conditional assembly pseudo-ops may not function */ -/* exactly as the DEC versions. I did not have any examples of these so */ -/* the implementation is my interpretation of how they should work. */ -/* */ -/* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ -/* file type that was specified on startup. This was intentional and */ -/* and allows rim formatted data to be output prior to the actual binary */ -/* formatted data. On UN*X style systems, the same effect can be achieved */ -/* by using the "cat" command, but on DOS/Windows systems, doing this was */ -/* a major chore. */ -/* */ -/* The floating point input does not generate values exactly as the DEC */ -/* compiler does. I worked out several examples by hand and believe that */ -/* this implementation is slightly more accurate. If I am mistaken, */ -/* let me know and, if possible, a better method of generating the values. */ -/* */ -/* BUILD and INSTALLATION */ -/* This program has been built and successfully executed on: */ -/* a. Linux (80486 CPU)using gcc */ -/* b. RS/6000 (AIX 3.2.5) */ -/* c. Borland C++ version 3.1 (large memory model) */ -/* d. Borland C++ version 4.52 (large memory model) */ -/* with no modifications to the source code. */ -/* */ -/* On UNIX type systems, store the the program as the pal command */ -/* and on PC type systems, store it as pal.exe */ -/* */ -/* HISTORICAL NOTE: */ -/* This assembler was written to support the fleet of PDP-8 systems */ -/* used by the Bay Area Rapid Transit System. As of early 1997, */ -/* this includes about 40 PDP-8/E systems driving the train destination */ -/* signs in passenger stations. */ -/* */ -/* REFERENCES: */ -/* This assembler is based on the pal assember by: */ -/* Douglas Jones and */ -/* Rich Coon */ -/* */ -/* DISCLAIMER: */ -/* See the symbol table for the set of pseudo-ops supported. */ -/* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ -/* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ -/* documentation of pseudo-ops. */ -/* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ -/* lower level introduction to the assembly language. */ -/* */ -/* WARRANTY: */ -/* If you don't like it the way it works or if it doesn't work, that's */ -/* tough. You're welcome to fix it yourself. That's what you get for */ -/* using free software. */ -/* */ -/* COPYRIGHT NOTICE: */ -/* This is free software. There is no fee for using it. You may make */ -/* any changes that you wish and also give it away. If you can make */ -/* a commercial product out of it, fine, but do not put any limits on */ -/* the purchaser's right to do the same. If you improve it or fix any */ -/* bugs, it would be nice if you told me and offered me a copy of the */ -/* new version. */ -/* */ -/* */ -/* Amendments Record: */ -/* Version Date by Comments */ -/* ------- ------- --- --------------------------------------------------- */ -/* v1.0 12Apr96 GAM Original */ -/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ -/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ -/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ -/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ -/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ -/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ -/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ -/* v3.0 14Feb97 RMS MACRO8X features. */ -/* v3.1 16Sep01 RMS Bug fixes: */ -/* - IFNZRO instead of IFNZERO */ -/* - allow blanks after symbol= */ -/* - remove field from label values */ -/* - don't include NOPUNCH data in checksum */ -/* */ -/******************************************************************************/ - -#include -#include -#include -#include - -#define LINELEN 96 -#define LIST_LINES_PER_PAGE 60 /* Includes 5 line page header. */ -#define NAMELEN 128 -#define SYMBOL_COLUMNS 5 -#define SYMLEN 7 -#define SYMBOL_TABLE_SIZE 8192 -#define MAC_MAX_ARGS 20 /* Must be < 26 */ -#define MAC_MAX_LENGTH 8192 -#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ -#define TITLELEN 63 -#define XREF_COLUMNS 8 - -#define ADDRESS_FIELD 00177 -#define FIELD_FIELD 070000 -#define INDIRECT_BIT 00400 -#define LAST_PAGE_LOC 00177 -#define OP_CODE 07000 -#define PAGE_BIT 00200 - -#ifdef PAGE_SIZE -#undef PAGE_SIZE -#endif -#define PAGE_SIZE 00200 - -#define PAGE_FIELD 07600 -#define PAGE_ZERO_END 00200 -#define TOTAL_PAGES (32 * 8) -#define GET_PAGE(x) (((x) >> 7) & (TOTAL_PAGES - 1)) - -/* Macro to get the number of elements in an array. */ -#define DIM(a) (sizeof(a)/sizeof(a[0])) - -/* Macro to get the address plus one of the end of an array. */ -#define BEYOND(a) ((a) + DIM(A)) - -#define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) -#define isend(c) ((c=='\0')|| (c=='\n')) -#define isdone(c) ((c=='/') || (isend(c)) || (c==';')) - -/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ -/* (true) if the stated condtion is met. */ -/* Use these to test attributes. The proper bits are extracted and then */ -/* tested. */ -#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) -#define M_DEFINED(s) ((s & DEFINED) == DEFINED) -#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) -#define M_FIXED(s) ((s & FIXED) == FIXED) -#define M_LABEL(s) ((s & LABEL) == LABEL) -#define M_MRI(s) ((s & MRI) == MRI) -#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) -#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) -#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) -#define M_MACRO(s) ((s & MACRO) == MACRO) -#define M_UNDEFINED(s) (!M_DEFINED(s)) -#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) - -/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ -#define M_DEF(s) (M_DEFINED(s)) -#define M_COND(s) (M_CONDITIONAL(s)) -#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) - -typedef unsigned char BOOL; -typedef unsigned char BYTE; -typedef int WORD32; - -#ifndef FALSE - #define FALSE 0 - #define TRUE (!FALSE) -#endif - -/* Line listing styles. Used to control listing of lines. */ -enum linestyle_t -{ - LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL -}; -typedef enum linestyle_t LINESTYLE_T; - -/* Symbol Types. */ -/* Note that the names that have FIX as the suffix contain the FIXED bit */ -/* included in the value. */ -/* */ -/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ -/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ -/* defined or undefined. The condition bit is set when the symbol is defined */ -/* during pass 1 and reset on pass 2 at the location the symbol was defined */ -/* during pass 1. When processing conditionals during pass 2, if the symbol */ -/* is defined and the condition bit is set, the symbol is treated as if it */ -/* were undefined. This gives consistent behavior of the conditional */ -/* pseudo-ops during both pass 1 and pass 2. */ -enum symtyp -{ - UNDEFINED = 0000, - DEFINED = 0001, - FIXED = 0002, - MRI = 0004 | DEFINED, - LABEL = 0010 | DEFINED, - REDEFINED = 0020 | DEFINED, - DUPLICATE = 0040 | DEFINED, - PSEUDO = 0100 | FIXED | DEFINED, - CONDITION = 0200, - MACRO = 0400 | DEFINED, - MRIFIX = MRI | FIXED | DEFINED, - DEFFIX = DEFINED | FIXED, - NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED -}; -typedef enum symtyp SYMTYP; - -enum pseudo_t -{ - BANK, BINPUNCH, DECIMAL, DEFINE, DUBL, EJECT, ENPUNCH, - EXPUNGE, FIELD, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, - IFZERO, LGM, LIST, LIT, LITBAS, NOLGM, NOPUNCH, - OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, TEXT, TITLE, - UNLIST, VFD, ZBLOCK -}; -typedef enum pseudo_t PSEUDO_T; - -struct sym_t -{ - SYMTYP type; - char name[SYMLEN]; - WORD32 val; - WORD32 xref_index; - WORD32 xref_count; -}; -typedef struct sym_t SYM_T; - -struct lpool_t -{ - WORD32 error; /* True if error message has been printed. */ - WORD32 pool[PAGE_SIZE]; -}; -typedef struct lpool_t LPOOL_T; - -struct emsg_t -{ - char *list; - char *file; -}; -typedef struct emsg_t EMSG_T; - -struct errsave_t -{ - char *mesg; - WORD32 col; -}; -typedef struct errsave_t ERRSAVE_T; - -struct fltg_ -{ - WORD32 exponent; - WORD32 mantissa; -}; -typedef struct fltg_ FLTG_T; - -/*----------------------------------------------------------------------------*/ - -/* Function Prototypes */ - -int binarySearch( char *name, int start, int symbol_count ); -int copyMacLine( int length, int from, int term, int nargs ); -int compareSymbols( const void *a, const void *b ); -void conditionFalse( void ); -void conditionTrue( void ); -SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); -void endOfBinary( void ); -void errorLexeme( EMSG_T *mesg, WORD32 col ); -void errorMessage( EMSG_T *mesg, WORD32 col ); -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); -SYM_T *eval( void ); -WORD32 evalDubl( WORD32 initial_value ); -FLTG_T *evalFltg( void ); -SYM_T *evalSymbol( void ); -void getArgs( int argc, char *argv[] ); -WORD32 getDublExpr( void ); -WORD32 getDublExprs( void ); -FLTG_T *getFltgExpr( void ); -FLTG_T *getFltgExprs( void ); -SYM_T *getExpr( void ); -WORD32 getExprs( void ); -WORD32 incrementClc( void ); -void inputDubl( void ); -void inputFltg( void ); -WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); -char *lexemeToName( char *name, WORD32 from, WORD32 term ); -void listLine( void ); -SYM_T *lookup( char *name ); -void moveToEndOfLine( void ); -void nextLexBlank( void ); -void nextLexeme( void ); -void normalizeFltg( FLTG_T *fltg ); -void onePass( void ); -void printCrossReference( void ); -void printErrorMessages( void ); -void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); -void printPageBreak( void ); -void printPermanentSymbolTable( void ); -void printSymbolTable( void ); -BOOL pseudoOperators( PSEUDO_T val ); -void punchChecksum( void ); -void punchLocObject( WORD32 loc, WORD32 val ); -void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); -void punchOutObject( WORD32 loc, WORD32 val ); -void punchLeader( WORD32 count ); -void punchObject( WORD32 val ); -void punchOrigin( WORD32 loc ); -void readLine( void ); -void saveError( char *mesg, WORD32 cc ); -BOOL testForLiteralCollision( WORD32 loc ); -BOOL testZeroPool( WORD32 value ); -void topOfForm( char *title, char *sub_title ); - -/*----------------------------------------------------------------------------*/ - -/* Table of pseudo-ops (directives) which are used to setup the symbol */ -/* table on startup and when the EXPUNGE pseudo-op is executed. */ -SYM_T pseudo[] = -{ - { PSEUDO, "BANK", BANK }, /* Synonym for field in MACRO8X. */ - { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ - { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ - { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ - { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ - { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ - { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ - { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ - { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ - { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ - { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ - { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ - { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ - { PSEUDO, "IFNZRO", IFNZERO }, /* Assemble if symbol value is not 0. */ - { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ - { PSEUDO, "LGM", LGM }, /* Enable link generation messages. */ - { PSEUDO, "LIST", LIST }, /* Enable listing. */ - { PSEUDO, "LIT", LIT }, /* Punch literal pool. */ - { PSEUDO, "LITBAS", LITBAS }, /* Set literal pool base. */ - { PSEUDO, "NOLGM", NOLGM }, /* Disable link generation messages. */ - { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ - { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ - { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ - { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ - { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ - { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ - { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ - { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ - { PSEUDO, "UNLIST", UNLIST }, /* Disable listing. */ - { PSEUDO, "VFD", VFD }, /* Variable field definition. */ - { PSEUDO, "ZBLOCK", ZBLOCK } /* Zero a block of memory. */ -}; - -/* Symbol Table */ -/* The table is put in lexical order on startup, so symbols can be */ -/* inserted as desired into the initial table. */ -SYM_T permanent_symbols[] = -{ - /* Memory Reference Instructions */ - { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ - { MRIFIX, "Z", 00000 }, /* PAGE ZERO ADDRESS */ - { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ - { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ - { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ - { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ - { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ - { MRIFIX, "JMP", 05000 }, /* JUMP */ - /* Floating Point Interpreter Instructions */ - { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ - { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ - { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ - { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ - { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ - { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ - { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ - { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ - { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ - { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ - { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ - /* Group 1 Operate Microinstrcutions */ - { FIXED, "OPR", 07000 }, /* OPERATE */ - { FIXED, "NOP", 07000 }, /* NO OPERATION */ - { FIXED, "IAC", 07001 }, /* INCREMENT AC */ - { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ - { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ - { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ - { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ - { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ - { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ - { FIXED, "CLL", 07100 }, /* CLEAR LINK */ - { FIXED, "CLA", 07200 }, /* CLEAR AC */ - /* Group 2 Operate Microinstructions */ - { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ - { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ - { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ - { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ - { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ - { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ - { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ - { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ - { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ - { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ - /* Combined Operate Microinstructions */ - { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ - { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ - { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ - { FIXED, "STA", 07240 }, /* SET AC TO -1 */ - { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ - /* MQ Instructions (PDP/8e) */ - { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ - { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ - /* Program Interrupt */ - { FIXED, "IOT", 06000 }, - { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ - { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ - /* Program Interrupt, PDP-8/e */ - { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ - { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ - { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ - { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ - { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ - { FIXED, "CAF", 06007 }, /* Clear all flags. */ - /* Keyboard/Reader */ - { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ - { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG */ - { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ - { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ - /* Teleprinter/Punch */ - { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ - { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ - { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ - { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ - /* High Speed Paper Tape Reader */ - { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ - { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ - { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ - /* PC8-E High Speed Paper Tape Reader & Punch */ - { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ - { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ - { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ - /* and fetch character. */ - /* High Speed Paper Tape Punch */ - { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ - { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ - { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ - { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ - /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ - { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ - /* into AC bits 0-9 */ - { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ - /* undisturbed */ - { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ - /* OR from AC. If AC bit 10=0, clear */ - /* error flags; if AC bit 11=0, DECtape */ - /* control flag is cleared. */ - { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ - { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ - /* control flag is 1 */ - { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ - /* ORed into AC */ - { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ - /* register B loaded from AC bits 6-8 */ - /* Disk File and Control, Type DF32 */ - { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ - /* INTERRUPT FLAGS */ - { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ - /* INTO CORE, CLEAR INTERRUPT FLAG */ - { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ - /* FROM CORE, CLEAR INTERRUPT FLAG */ - { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ - { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ - /* MEMORY ADDRESS EXTENSION REGISTER */ - { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ - /* MEMORY ADDRESS EXTENSION REGISTER */ - /* AND LOAD SAME FROM AC */ - { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ - /* ADDRESS REGISTER, SKIP IF ADDRESS */ - /* CONFIRMED FLAG = 1 */ - { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ - /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ - /* (NO ERROR) */ - { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ - /* TRANSFER COMPLETE) */ - { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ - /* ADDRESS REGISTER */ - /* Disk File and Control, Type RF08 */ - { FIXED, "DCIM", 06611 }, - { FIXED, "DIML", 06615 }, - { FIXED, "DIMA", 06616 }, - /*{ FIXED, "DISK", 06623 },*/ - { FIXED, "DCXA", 06641 }, - { FIXED, "DXAL", 06643 }, - { FIXED, "DXAC", 06645 }, - { FIXED, "DMMT", 06646 }, - /* Memory Extension Control, Type 183 */ - { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ - { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ - { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ - { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ - { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ - { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ - { FIXED, "RMF", 06244 }, /* RESTORE MEMORY FIELD */ - /* Memory Parity, Type MP8/I (MP8/L) */ - { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ - { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ - /* Memory Parity, Type MP8-E (PDP8/e) */ - { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ - { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ - { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ - { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ - { FIXED, "CEP", 06106 }, /* Check for even parity. */ - { FIXED, "SPO", 06107 }, /* Skip on parity option. */ -}; /* End-of-Symbols for Permanent Symbol Table */ - -/* Global variables */ -SYM_T *symtab; /* Symbol Table */ -int symbol_top; /* Number of entries in symbol table. */ - -SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ -int number_of_fixed_symbols; - -/*----------------------------------------------------------------------------*/ - -WORD32 *xreftab; /* Start of the concordance table. */ - -ERRSAVE_T error_list[20]; -int save_error_count; - -LPOOL_T pz; /* Storage for page zero constants. */ -LPOOL_T cp; /* Storage for current page constants. */ -WORD32 lit_base[TOTAL_PAGES]; /* Literal base address for all pages. */ -WORD32 lit_loc[TOTAL_PAGES]; /* Literal current location for all pages. */ - -char s_detected[] = "detected"; -char s_error[] = "error"; -char s_errors[] = "errors"; -char s_no[] = "No"; -char s_page[] = "Page"; -char s_symtable[] = "Symbol Table"; -char s_xref[] = "Cross Reference"; - -/* Assembler diagnostic messages. */ -/* Some attempt has been made to keep continuity with the PAL-III and */ -/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ -/* exists, then the indicator is put in the listing as the first two */ -/* characters of the diagnostic message. The PAL-III indicators where used */ -/* when there was a choice between using MACRO-8 and PAL-III indicators. */ -/* The character pairs and their meanings are: */ -/* DT Duplicate Tag (symbol) */ -/* IC Illegal Character */ -/* ID Illegal Redefinition of a symbol. An attempt was made to give */ -/* a symbol a new value not via =. */ -/* IE Illegal Equals An equal sign was used in the wrong context, */ -/* (e.g., A+B=C, or TAD A+=B) */ -/* II Illegal Indirect An off page reference was made, but a literal */ -/* could not be generated because the indirect bit was already set. */ -/* IR Illegal Reference (address is not on current page or page zero) */ -/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ -/* RD ReDefintion of a symbol */ -/* ST Symbol Table full */ -/* UA Undefined Address (undefined symbol) */ -/* ZE Zero Page Exceeded (see above, or out of space) */ -EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; -EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; -EMSG_T illegal_character = { "IC illegal char", "illegal character" }; -EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; -EMSG_T label_syntax = { "IC label syntax", "label syntax" }; -EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; -EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; -EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; -EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; -EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; -EMSG_T illegal_reference = { "IR off page", "illegal reference" }; -EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; -EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; -EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; -EMSG_T literal_overflow = { "PE page exceeded", - "current page literal capacity exceeded" }; -EMSG_T pz_literal_overflow = { "ZE page exceeded", - "page zero capacity exceeded" }; -EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; -EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; -EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; -EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; -EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; -EMSG_T illegal_field_value = { "expr out of range", - "field value not in range of 0 through 7" }; -EMSG_T illegal_vfd_value = { "width out of range", - "VFD field width not in range" }; -EMSG_T no_literal_value = { "no value", "No literal value" }; -EMSG_T text_string = { "no delimiter", - "Text string delimiters not matched" }; -EMSG_T in_rim_mode = { "not OK in rim mode" - "FIELD pseudo-op not valid in RIM mode" }; -EMSG_T lt_expected = { "'<' expected", "'<' expected" }; -EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; -EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; -EMSG_T bad_dummy_arg = { "bad dummy arg", - "Bad dummy argument following DEFINE" }; -EMSG_T macro_too_long = { "macro too long", "Macro too long" }; -EMSG_T no_virtual_memory = { "out of memory", - "Insufficient memory for macro" }; -EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; - -/*----------------------------------------------------------------------------*/ - -FILE *errorfile; -FILE *infile; -FILE *listfile; -FILE *listsave; -FILE *objectfile; -FILE *objectsave; - -char errorpathname[NAMELEN]; -char filename[NAMELEN]; -char listpathname[NAMELEN]; -char objectpathname[NAMELEN]; -char *pathname; -char permpathname[NAMELEN]; - -char mac_buffer[MAC_MAX_LENGTH + 1]; -char *mac_bodies[MAC_TABLE_LENGTH]; -char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; -int mac_arg_pos[26] = { 0 }; - -int list_lineno; -int list_pageno; -char list_title[4*LINELEN]; -BOOL list_title_set; /* Set if TITLE pseudo-op used. */ -char line[4*LINELEN]; /* Input line. */ -int lineno; /* Current line number. */ -char mac_line[4*LINELEN]; /* Saved macro invocation line. */ -int page_lineno; /* print line number on current page. */ -WORD32 listed; /* Listed flag. */ -WORD32 listedsave; - -WORD32 cc; /* Column Counter (char position in line). */ -WORD32 checksum; /* Generated checksum */ -BOOL binary_data_output; /* Set true when data has been output. */ -WORD32 clc; /* Location counter */ -char delimiter; /* Character immediately after eval'd term. */ -int errors; /* Number of errors found so far. */ -BOOL error_in_line; /* TRUE if error on current line. */ -int errors_pass_1; /* Number of errors on pass 1. */ -WORD32 field; /* Current field */ -WORD32 fieldlc; /* location counter without field portion. */ -int filix_curr; /* Index in argv to current input file. */ -int filix_start; /* Start of input files in argv. */ -BOOL fltg_input; /* TRUE when doing floating point input. */ -BOOL indirect_generated; /* TRUE if an off page address generated. */ -WORD32 lexstartprev; /* Where previous lexeme started. */ -WORD32 lextermprev; /* Where previous lexeme ended. */ -WORD32 lexstart; /* Index of current lexeme on line. */ -WORD32 lexterm; /* Index of character after current lexeme. */ -WORD32 mac_cc; /* Saved cc after macro invocation. */ -WORD32 mac_count; /* Total macros defined. */ -BOOL nomac_exp; /* Print macro expansions. */ -char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ -WORD32 maxcc; /* Current line length. */ -BOOL lgm_flag; /* Link generated messages enable flag. */ -BOOL overflow; /* Overflow flag for math routines. */ -WORD32 pass; /* Number of current pass. */ -BOOL print_permanent_symbols; -WORD32 radix; /* Default number radix. */ -WORD32 reloc; /* The relocation distance. */ -BOOL rim_mode; /* Generate rim format, defaults to bin */ -int save_argc; /* Saved argc. */ -char **save_argv; /* Saved *argv[]. */ -BOOL symtab_print; /* Print symbol table flag */ -BOOL xref; - -FLTG_T fltg_ac; /* Value holder for evalFltg() */ -SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ -SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ -SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ - - -/******************************************************************************/ -/* */ -/* Function: main */ -/* */ -/* Synopsis: Starting point. Controls order of assembly. */ -/* */ -/******************************************************************************/ -int main( int argc, char *argv[] ) -{ - int ix; - int space; - - save_argc = argc; - save_argv = argv; - - /* Set the default values for global symbols. */ - binary_data_output = FALSE; - fltg_input = FALSE; - nomac_exp = TRUE; - print_permanent_symbols = FALSE; - rim_mode = FALSE; - symtab_print = FALSE; - xref = FALSE; - pathname = NULL; - for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) - { - mac_bodies[ix] = NULL; - } - - /* Get the options and pathnames */ - getArgs( argc, argv ); - - /* Setup the error file in case symbol table overflows while installing the */ - /* permanent symbols. */ - errorfile = fopen( errorpathname, "w" ); - errors = 0; - save_error_count = 0; - pass = 0; /* This is required for symbol table initialization. */ - symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); - - if( symtab == NULL ) - { - fprintf( stderr, "Could not allocate memory for symbol table.\n"); - exit( -1 ); - } - - /* Place end marker in symbol table. */ - symtab[0] = sym_undefined; - symbol_top = 0; - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Enter the pseudo-ops into the symbol table */ - for( ix = 0; ix < DIM( pseudo ); ix++ ) - { - defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); - } - - /* Enter the predefined symbols into the table. */ - /* Also make them part of the permanent symbol table. */ - for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) - { - defineSymbol( permanent_symbols[ix].name, - permanent_symbols[ix].val, - permanent_symbols[ix].type | DEFFIX , 0 ); - } - - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Do pass one of the assembly */ - checksum = 0; - pass = 1; - onePass(); - errors_pass_1 = errors; - fclose ( errorfile ); - - /* Set up for pass two */ - errorfile = fopen( errorpathname, "w" ); - objectfile = fopen( objectpathname, "wb" ); - objectsave = objectfile; - - listfile = fopen( listpathname, "w" ); - listsave = listfile; - - punchLeader( 0 ); - checksum = 0; - - /* Do pass two of the assembly */ - errors = 0; - save_error_count = 0; - - if( xref ) - { - /* Get the amount of space that will be required for the concordance. */ - for( space = 0, ix = 0; ix < symbol_top; ix++ ) - { - symtab[ix].xref_index = space; /* Index into concordance table. */ - space += symtab[ix].xref_count + 1; - symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ - - } - /* Allocate the necessary space. */ - xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); - - /* Clear the cross reference space. */ - for( ix = 0; ix < space; ix++ ) - { - xreftab[ix] = 0; - } - } - pass = 2; - onePass(); - - /* Undo effects of NOPUNCH for any following checksum */ - objectfile = objectsave; - punchChecksum(); - - /* Works great for trailer. */ - punchLeader( 1 ); - - /* undo effects of NOLIST for any following output to listing file. */ - listfile = listsave; - - /* Display value of error counter. */ - if( errors == 0 ) - { - fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); - } - else - { - fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - fprintf( listfile, "\n %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - fprintf( stderr, " %d %s %s\n", errors, s_detected, - ( errors == 1 ? s_error : s_errors )); - } - - if( symtab_print ) - { - printSymbolTable(); - } - - if( print_permanent_symbols ) - { - printPermanentSymbolTable(); - } - - if( xref ) - { - printCrossReference(); - } - - fclose( objectfile ); - fclose( listfile ); - fclose( errorfile ); - if( errors == 0 && errors_pass_1 == 0 ) - { - remove( errorpathname ); - } - - return( errors != 0 ); -} /* main() */ - -/******************************************************************************/ -/* */ -/* Function: getArgs */ -/* */ -/* Synopsis: Parse command line, set flags accordingly and setup input and */ -/* output files. */ -/* */ -/******************************************************************************/ -void getArgs( int argc, char *argv[] ) -{ - WORD32 len; - WORD32 ix, jx; - - /* Set the defaults */ - errorfile = NULL; - infile = NULL; - listfile = NULL; - listsave = NULL; - objectfile = NULL; - objectsave = NULL; - - for( ix = 1; ix < argc; ix++ ) - { - if( argv[ix][0] == '-' ) - { - for( jx = 1; argv[ix][jx] != 0; jx++ ) - { - switch( argv[ix][jx] ) - { - case 'd': - symtab_print = TRUE; - break; - - case 'm': - nomac_exp = FALSE; - break; - - case 'r': - rim_mode = TRUE; - break; - - case 'p': - print_permanent_symbols = TRUE; - break; - - case 'x': - xref = TRUE; - break; - - default: - fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); - fprintf( stderr, " -d -- dump symbol table\n" ); - fprintf( stderr, " -m -- print macro expansions\n" ); - fprintf( stderr, " -r -- output rim format file\n" ); - fprintf( stderr, " -p -- output permanent symbols to file\n" ); - fprintf( stderr, " -x -- output cross reference to file\n" ); - fflush( stderr ); - exit( -1 ); - } /* end switch */ - } /* end for */ - } - else - { - filix_start = ix; - pathname = argv[ix]; - break; - } - } /* end for */ - - if( pathname == NULL ) - { - fprintf( stderr, "%s: no input file specified\n", argv[0] ); - exit( -1 ); - } - - len = strlen( pathname ); - if( len > NAMELEN - 5 ) - { - fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); - exit( -1 ); - } - - /* Now make the pathnames */ - /* Find last '.', if it exists. */ - jx = len - 1; - while( pathname[jx] != '.' && pathname[jx] != '/' - && pathname[jx] != '\\' && jx >= 0 ) - { - jx--; - } - - switch( pathname[jx] ) - { - case '.': - break; - - case '/': - case '\\': - jx = len; - break; - - default: - break; - } - - /* Add the pathname extensions. */ - strncpy( objectpathname, pathname, jx ); - objectpathname[jx] = '\0'; - strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); - - strncpy( listpathname, pathname, jx ); - listpathname[jx] = '\0'; - strcat( listpathname, ".lst" ); - - strncpy( errorpathname, pathname, jx ); - errorpathname[jx] = '\0'; - strcat( errorpathname, ".err" ); - - strncpy( permpathname, pathname, jx ); - permpathname[jx] = '\0'; - strcat( permpathname, ".prm" ); - - /* Extract the filename from the path. */ - if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) - { - pathname[1] = '\\'; /* MS-DOS style pathname */ - } - - jx = len - 1; - while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) - { - jx--; - } - strcpy( filename, &pathname[jx + 1] ); - -} /* getArgs() */ - - -/******************************************************************************/ -/* */ -/* Function: onePass */ -/* */ -/* Synopsis: Do one assembly pass. */ -/* */ -/******************************************************************************/ -void onePass() -{ - BOOL blanks; - int ix; - int jx; - char name[SYMLEN]; - WORD32 newclc; - BOOL scanning_line; - WORD32 start; - SYM_T *sym; - WORD32 term; - WORD32 val; - - clc = 0200; /* Default starting address is 200 octal. */ - field = 0; - fieldlc = 0200; - reloc = 0; - for( ix = 0; ix < TOTAL_PAGES; ix++ ) - { - lit_loc[ix] = lit_base[ix] = 00200; - } - mac_count = 0; /* No macros defined. */ - mac_ptr = NULL; /* Not in a macro. */ - for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) - { - if ( mac_bodies[ix] ) - { - free( mac_bodies[ix] ); - } - } - cp.error = FALSE; - pz.error = FALSE; - listed = TRUE; - lgm_flag = TRUE; - lineno = 0; - list_pageno = 0; - list_lineno = 0; - list_title_set = FALSE; - page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ - radix = 8; /* Initial radix is octal (base 8). */ - - /* Now open the first input file. */ - filix_curr = filix_start; /* Initialize pointer to input files. */ - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) - { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); - exit( -1 ); - } - - while( TRUE ) - { - readLine(); - nextLexeme(); - - scanning_line = TRUE; - while( scanning_line ) - { - if( isend( line[lexstart] )) - { - scanning_line = FALSE; - } - else - { - switch( line[lexstart] ) - { - case '/': - scanning_line = FALSE; - break; - - case ';': - nextLexeme(); - break; - - case '$': - endOfBinary(); - fclose( infile ); - return; - - case '*': - nextLexeme(); /* Skip '*', (set origin symbol) */ - newclc = ((getExpr())->val & 07777 ) | field; - /* Do not change Current Location Counter if an error occurred. */ - if( !error_in_line ) - { - if(( newclc & 07600 ) != ( clc & 07600 )) - { - /* Current page has changed. */ - punchLiteralPool( &cp, clc - 1 ); - } - clc = newclc - reloc; - fieldlc = clc & 07777; - - if( !rim_mode ) - { - /* Not rim mode, put out origin. */ - punchOrigin( clc ); - } - printLine( line, 0, fieldlc, LINE_VAL ); - } - break; - - default: - switch( line[lexterm] ) - { - case ',': - if( isalpha( line[lexstart] )) - { - /* Use lookup so symbol will not be counted as reference. */ - sym = lookup( lexemeToName( name, lexstart, lexterm )); - if( M_DEFINED( sym->type )) - { - if( sym->val != (clc & 07777) && pass == 2 ) - { - errorSymbol( &duplicate_label, sym->name, lexstart ); - } - sym->type = sym->type | DUPLICATE; - } - /* Must call define on pass 2 to generate concordance. */ - defineLexeme( lexstart, lexterm, ( clc+reloc ), LABEL ); - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - nextLexeme(); /* skip label */ - nextLexeme(); /* skip comma */ - break; - - case '=': - if( isalpha( line[lexstart] )) - { - start = lexstart; - term = lexterm; - delimiter = line[lexterm]; - nextLexBlank(); /* skip symbol */ - nextLexeme(); /* skip trailing = */ - val = getExprs(); - defineLexeme( start, term, val, DEFINED ); - printLine( line, 0, val, LINE_VAL ); - } - else - { - errorLexeme( &symbol_syntax, lexstartprev ); - nextLexeme(); /* skip symbol */ - nextLexeme(); /* skip trailing = */ - getExprs(); /* skip expression */ - } - break; - - default: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - val = sym->val; - if( M_MACRO( sym->type )) - { /* Find arguments. */ - blanks = TRUE; /* Expecting blanks. */ - for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) - { - if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; - else if( blanks ) - { - mac_arg_pos[jx++] = cc; - blanks = FALSE; - } - } /* end for */ - for( ; jx < MAC_MAX_ARGS; jx++ ) - { - mac_arg_pos[jx] = 0; - } - for( jx = 0; jx < LINELEN; jx++ ) - { - mac_line[jx] = line[jx]; - } - mac_cc = cc; /* Save line and position in line. */ - mac_ptr = mac_bodies[val]; - if( mac_ptr ) scanning_line = FALSE; - else nextLexeme(); - } /* end if macro */ - else if( M_PSEUDO( sym->type )) - { - nextLexeme(); /* Skip symbol */ - scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); - } - else - { - /* Identifier is not a pseudo-op, interpret as load value */ - punchOutObject( clc, getExprs() & 07777 ); - incrementClc(); - } - } - else - { - /* Identifier is a value, interpret as load value */ - punchOutObject( clc, getExprs() & 07777 ); - incrementClc(); - } - break; - } /* end switch */ - break; - } /* end switch */ - } /* end if */ - } /* end while( scanning_line ) */ - } /* end while( TRUE ) */ -} /* onePass() */ - - -/******************************************************************************/ -/* */ -/* Function: getExprs */ -/* */ -/* Synopsis: Or together a list of blank separated expressions, from the */ -/* current lexeme onward. Leave the current lexeme as */ -/* the last one in the list. */ -/* */ -/******************************************************************************/ -WORD32 getExprs() -{ - SYM_T *symv; - SYM_T *symt; - WORD32 temp; - SYMTYP temp_type; - WORD32 value; - SYMTYP value_type; - - symv = getExpr(); - value = symv->val; - value_type = symv->type; - - while( TRUE ) - { - if( isdone( line[lexstart] )) - { - return( value ); - } - switch( line[lexstart] ) - { - case ')': - case ']': - return( value ); - - default: - break; - } - - /* Interpret space as logical or */ - symt = getExpr(); - temp = symt->val & 07777; - temp_type = symt->type; - - switch( value_type ) - { - case MRI: - case MRIFIX: - /* Previous symbol was a Memory Reference Instruction. */ - switch( temp_type ) - { - case MRI: - case MRIFIX: - /* Current symbol is also a Memory Reference Instruction. */ - value |= temp; /* Just OR the MRI instructions. */ - break; - - default: - /* Now have the address part of the MRI instruction. */ - if( temp < 00200 ) - { - value |= temp; /* Page zero MRI. */ - } - else if( (( fieldlc + reloc ) & 07600 ) <= temp - && temp <= (( fieldlc + reloc ) | 0177 )) - { - value |= ( PAGE_BIT | (temp & ADDRESS_FIELD )); /* Current page MRI */ - } - else - { - if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) - { - /* Already indirect, can't generate */ - errorSymbol( &illegal_indirect, symt->name, lexstartprev ); - } - else - { - /* Now fix off page reference. */ - /* Search current page literal pool for needed value. */ - /* Set Indirect Current Page */ - if( testZeroPool( temp )) - { - value |= ( 00400 | insertLiteral( &pz, field, temp )); - } - else - { - value |= ( 00600 | insertLiteral( &cp, clc, temp )); - } - indirect_generated = TRUE; - } - } - break; - } - break; - - default: - value |= temp; /* Normal 12 bit value. */ - break; - } - } /* end while */ -} /* getExprs() */ - - -/******************************************************************************/ -/* */ -/* Function: getExpr */ -/* */ -/* Synopsis: Get an expression, from the current lexeme onward, leave the */ -/* current lexeme as the one after the expression. Expressions */ -/* contain terminal symbols (identifiers) separated by operators. */ -/* */ -/******************************************************************************/ -SYM_T *getExpr() -{ - delimiter = line[lexterm]; - - if( line[lexstart] == '-' ) - { - nextLexBlank(); - sym_getexpr = *(eval()); - sym_getexpr.val = ( - sym_getexpr.val ); - } - else - { - sym_getexpr = *(eval()); - } - - - if( is_blank( delimiter )) - { - return( &sym_getexpr ); - } - - /* Here we assume the current lexeme is the operator separating the */ - /* previous operator from the next, if any. */ - while( TRUE ) - { - /* assert line[lexstart] == delimiter */ - if( is_blank( delimiter )) - { - return( &sym_getexpr ); - } - - switch( line[lexstart] ) - { - case '+': /* add */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val += (eval())->val; - break; - - case '-': /* subtract */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val -= (eval())->val; - break; - - case '^': /* multiply */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val *= (eval())->val; - break; - - case '%': /* divide */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val /= (eval())->val; - break; - - case '&': /* and */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val &= (eval())->val; - break; - - case '!': /* or */ - nextLexBlank(); /* skip over the operator */ - sym_getexpr.val |= (eval())->val; - break; - - default: - if( isend( line[lexstart] )) - { - return( &sym_getexpr ); - } - - switch( line[lexstart] ) - { - case '/': - case ';': - case ')': - case ']': - case '<': - case ':': - case ',': - break; - - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - sym_getexpr.val = 0; - break; - - default: - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - sym_getexpr.val = 0; - break; - } - return( &sym_getexpr ); - } - } /* end while */ -} /* getExpr() */ - - -/******************************************************************************/ -/* */ -/* Function: eval */ -/* */ -/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ -/* */ -/******************************************************************************/ -SYM_T *eval() -{ - WORD32 digit; - WORD32 from; - WORD32 loc; - SYM_T *sym; - WORD32 val; - - val = 0; - - delimiter = line[lexterm]; - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - if( M_UNDEFINED( sym->type )) - { - if( pass == 2 ) - { - errorSymbol( &undefined_symbol, sym->name, lexstart ); - } - nextLexeme(); - return( sym ); - } - else if( M_PSEUDO( sym->type )) - { - if( sym->val == DECIMAL ) - { - radix = 10; - } - else if( sym->val == OCTAL ) - { - radix = 8; - } - else if( pass == 2 ) - { - errorSymbol( &misplaced_symbol, sym->name, lexstart ); - } - sym_eval.type = sym->type; - sym_eval.val = 0; - nextLexeme(); - return( &sym_eval ); - } - else if( M_MACRO( sym->type )) - { - if( pass == 2 ) - { - errorSymbol( &misplaced_symbol, sym->name, lexstart ); - } - sym_eval.type = sym->type; - sym_eval.val = 0; - nextLexeme(); - return( &sym_eval ); - } - else - { - nextLexeme(); - return( sym ); - } - } - else if( isdigit( line[lexstart] )) - { - from = lexstart; - val = 0; - while( from < lexterm ) - { - if( isdigit( line[from] )) - { - digit = (WORD32) line[from++] - (WORD32) '0'; - if( digit < radix ) - { - val = val * radix + digit; - } - else - { - errorLexeme( &number_not_radix, from - 1 ); - val = 0; - from = lexterm; - } - } - else - { - errorLexeme( ¬_a_number, lexstart ); - val = 0; - from = lexterm; - } - } - nextLexeme(); - sym_eval.val = val; - return( &sym_eval ); - } - else - { - switch( line[lexstart] ) - { - case '"': /* Character literal */ - if( lexstart + 2 < maxcc ) - { - val = line[lexstart + 1] | 0200; - delimiter = line[lexstart + 2]; - cc = lexstart + 2; - } - else - { - errorMessage( &no_literal_value, lexstart ); - } - nextLexeme(); - break; - - case '.': /* Value of Current Location Counter */ - val = clc + reloc; - nextLexeme(); - break; - - case '[': /* Generate literal on page zero. */ - nextLexBlank(); /* Skip bracket */ - val = getExprs() & 07777; - if( line[lexstart] == ']' ) - { - delimiter = line[lexterm]; - nextLexeme(); /* Skip end bracket */ - } - else - { - /* errorMessage( "parens", lexstart ); */ - } - sym_eval.val = insertLiteral( &pz, field, val ); - return( &sym_eval ); - - case '(': /* Generate literal on current page. */ - nextLexBlank(); /* Skip paren */ - val = getExprs() & 07777; - - if( line[lexstart] == ')' ) - { - delimiter = line[lexterm]; - nextLexeme(); /* Skip end paren */ - } - else - { - /* errorMessage( "parens", NULL ); */ - } - if( testZeroPool( val )) - { - sym_eval.val = insertLiteral( &pz, field, val ); - } - else - { - loc = insertLiteral( &cp, clc, val ); - sym_eval.val = loc + (( clc + reloc ) & 077600 ); - } - return( &sym_eval ); - - default: - switch( line[lexstart] ) - { - case '=': - errorMessage( &illegal_equals, lexstart ); - moveToEndOfLine(); - break; - - default: - errorMessage( &illegal_character, lexstart ); - break; - } - val = 0; /* On error, set value to zero. */ - nextLexBlank(); /* Go past illegal character. */ - } - } - sym_eval.val = val; - return( &sym_eval ); -} /* eval() */ - - -/******************************************************************************/ -/* */ -/* Function: inputDubl */ -/* */ -/* Synopsis: Get the value of the current lexeme as a double word. */ -/* */ -/******************************************************************************/ -void inputDubl() -{ - WORD32 dublvalue; - BOOL scanning_line; - - scanning_line = TRUE; - do - { - while( scanning_line ) - { - if( isend( line[lexstart] )) - { - scanning_line = FALSE; - } - else - { - switch( line[lexstart] ) - { - case '/': - scanning_line = FALSE; - break; - - case ';': - nextLexeme(); - break; - - case '+': - delimiter = line[lexterm]; - nextLexBlank(); - case '-': - default: - if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) - { - dublvalue = getDublExprs(); - punchOutObject( clc, (WORD32)(( dublvalue >> 12 ) & 07777 )); - incrementClc(); - punchOutObject( clc, (WORD32)( dublvalue & 07777 )); - incrementClc(); - } - else - { - return; /* Non-numeric input, back to assembly. */ - } - break; - } /* end switch */ - } /* end if */ - - if( error_in_line ) - { - return; /* Error occurred, exit DUBL input mode. */ - } - } /* end while( scanning_line ) */ - readLine(); - nextLexeme(); - scanning_line = TRUE; - } - while( TRUE ); -} /* inputDubl() */ - - -/******************************************************************************/ -/* */ -/* Function: getDublExprs */ -/* */ -/* Synopsis: Get a DUBL expression. */ -/* */ -/******************************************************************************/ -WORD32 getDublExprs() -{ - WORD32 dublvalue; - - dublvalue = getDublExpr(); - - while( TRUE ) - { - if( isdone( line[lexstart] )) - { - return( dublvalue ); - } - else - { - errorMessage( &illegal_expression, lexstart - 1 ); - return( 0 ); - } - } /* end while */ -} /* getDublExprs() */ - - -/******************************************************************************/ -/* */ -/* Function: getDublExpr */ -/* */ -/* Synopsis: Get the value of the current lexeme as a double word. The */ -/* number is always considered to have a decimal radix. */ -/* */ -/******************************************************************************/ -WORD32 getDublExpr() -{ - WORD32 dublvalue; - - delimiter = line[lexterm]; - if( line[lexstart] == '-' ) - { - nextLexBlank(); - dublvalue = evalDubl( 0 ); - nextLexeme(); - /* Test for any value greater than 23 bits in length. */ - if( (unsigned long int)dublvalue > 040000000L ) - { - errorMessage( &dubl_overflow, lexstart ); - dublvalue = 0; - } - dublvalue = -dublvalue; - } - else - { - dublvalue = evalDubl( 0 ); - nextLexeme(); - /* Test for any value greater than 23 bits in length. */ - if( (unsigned long int)dublvalue > 037777777L ) - { - errorMessage( &dubl_overflow, lexstart ); - dublvalue = 0; - } - } - - if( is_blank( delimiter )) - { - return( dublvalue ); - } - - /* Here we assume the current lexeme is the operator separating the */ - /* previous operator from the next, if any. */ - while( TRUE ) - { - /* assert line[lexstart] == delimiter */ - if( is_blank( delimiter )) - { - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - dublvalue = 0; - return( dublvalue ); - } - - switch( line[lexstart] ) - { - case '+': /* add */ - case '-': /* subtract */ - case '^': /* multiply */ - case '%': /* divide */ - case '&': /* and */ - case '!': /* or */ - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - dublvalue = 0; - break; - - default: - if( isend( line[lexstart] )) - { - return( dublvalue ); - } - - switch( line[lexstart] ) - { - case '/': - case ';': - break; - - default: - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - dublvalue = 0; - break; - } - return( dublvalue ); - } - } /* end while */ -} /* getDublExpr() */ - - -/******************************************************************************/ -/* */ -/* Function: evalDubl */ -/* */ -/* Synopsis: Get the value of the current lexeme as a double word. The */ -/* number is always considered to have a decimal radix. */ -/* */ -/******************************************************************************/ -WORD32 evalDubl( WORD32 initial_value ) -{ - WORD32 digit; - WORD32 from; - WORD32 dublvalue; - WORD32 olddublvalue; - - overflow = FALSE; - delimiter = line[lexterm]; - from = lexstart; - dublvalue = initial_value; - - while( from < lexterm ) - { - if( isdigit( line[from] )) - { - olddublvalue = dublvalue; - digit = (WORD32)( line[from++] - '0' ); - dublvalue = dublvalue * 10 + digit; - if( dublvalue < olddublvalue ) - { - overflow = TRUE; - } - } - else - { - errorLexeme( ¬_a_number, from ); - dublvalue = 0; - from = lexterm; - } - } - return( dublvalue ); -} /* evalDubl() */ - - -/******************************************************************************/ -/* */ -/* Function: inputFltg */ -/* */ -/* Synopsis: Get the value of the current lexeme as a Floating Point const. */ -/* */ -/******************************************************************************/ -void inputFltg() -{ - FLTG_T *fltg; - BOOL scanning_line; - - fltg_input = TRUE; /* Set lexeme scanner for floating point. */ - scanning_line = TRUE; - while( TRUE ) - { - while( scanning_line ) - { - if( isend( line[lexstart] )) - { - scanning_line = FALSE; - } - else - { - switch( line[lexstart] ) - { - case '/': - scanning_line = FALSE; - break; - - case ';': - nextLexeme(); - break; - - case '+': - delimiter = line[lexterm]; - nextLexBlank(); - case '-': - default: - if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) - { - fltg = getFltgExprs(); - punchOutObject( clc, ( fltg->exponent & 07777 )); - incrementClc(); - punchOutObject( clc, (WORD32)(( fltg->mantissa >> 12 ) & 07777 )); - incrementClc(); - punchOutObject( clc, (WORD32)( fltg->mantissa & 07777 )); - incrementClc(); - } - else - { - fltg_input = FALSE; /* Reset lexeme scanner. */ - return; /* Non-numeric input, back to assembly. */ - } - break; - } /* end switch */ - } /* end if */ - - if( error_in_line ) - { - fltg_input = FALSE; /* Reset lexeme scanner. */ - return; /* Error occurred, exit FLTG input mode. */ - } - } /* end while( scanning_line ) */ - readLine(); - nextLexeme(); - scanning_line = TRUE; - } -} /* inputFltg() */ - - -/******************************************************************************/ -/* */ -/* Function: getFltgExprs */ -/* */ -/* Synopsis: Get a FLTG expression. */ -/* */ -/******************************************************************************/ -FLTG_T *getFltgExprs() -{ - FLTG_T *fltg; - - fltg = getFltgExpr(); - - while( TRUE ) - { - if( isdone( line[lexstart] )) - { - return( fltg ); - } - else - { - errorMessage( &illegal_expression, lexstart - 1 ); - return( 0 ); - } - } /* end while */ -} /* getFltgExprs() */ - - -/******************************************************************************/ -/* */ -/* Function: getFltgExpr */ -/* */ -/* Synopsis: Get the value of the current lexeme as a double word. The */ -/* number is always considered to have a decimal radix. */ -/* */ -/******************************************************************************/ -FLTG_T *getFltgExpr() -{ - FLTG_T *fltg; - - delimiter = line[lexterm]; - fltg = evalFltg(); - /* Test for any value greater than 23 bits in length. */ - if( (unsigned long int)fltg->mantissa> 077777777L ) - { - errorMessage( &fltg_overflow, lexstart ); - } - - if( is_blank( delimiter )) - { - return( fltg ); - } - - /* Here we assume the current lexeme is the operator separating the */ - /* previous operator from the next, if any. */ - while( TRUE ) - { - /* assert line[lexstart] == delimiter */ - if( is_blank( delimiter )) - { - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - fltg = 0; - return( fltg ); - } - - switch( line[lexstart] ) - { - case '+': /* add */ - case '-': /* subtract */ - case '^': /* multiply */ - case '%': /* divide */ - case '&': /* and */ - case '!': /* or */ - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - fltg = NULL; - break; - - default: - if( isend( line[lexstart] )) - { - return( fltg ); - } - - switch( line[lexstart] ) - { - case '/': - case ';': - break; - - default: - errorMessage( &illegal_expression, lexstart ); - moveToEndOfLine(); - fltg = NULL; - break; - } - return( fltg ); - } - } /* end while */ -} /* getFltgExpr() */ - - -/******************************************************************************/ -/* */ -/* Function: evalFltg */ -/* */ -/* Synopsis: Get the value of the current lexeme as a floating point value. */ -/* Floating point input is alwasy considered decimal. */ -/* The general format of a floating point number is: */ -/* +-ddd.dddE+-dd where each d is a decimal digit. */ -/* */ -/******************************************************************************/ -FLTG_T *evalFltg() -{ - BYTE current_state; - BYTE current_col; - WORD32 exponent; - FLTG_T *fltg; - WORD32 input_value; - BOOL negate; - BOOL negate_exponent; - BYTE next_state; - int right_digits; - - /* This uses a lexical analyzer to parse the floating point format. */ - static BYTE state_table[][10] = - { - /* 0 1 2 3 4 5 6 Oolumn index */ - /* + - d . E sp p State Comment */ - { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ - { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ - { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ - { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ - { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ - { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ - { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ - { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ - { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ - { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ - /* 10 Completion state */ - /* 11 Error state. */ - }; - - delimiter = line[lexterm]; - fltg = &fltg_ac; - fltg->exponent = 0; - fltg->mantissa = 0; - input_value = 0; - negate = FALSE; - negate_exponent = FALSE; - exponent = 0; - right_digits = 0; - current_state = 0; - - while( TRUE ) - { - /* Classify character. This is the column index. */ - switch( line[lexstart] ) - { - case '+': - current_col = 0; - break; - - case '-': - current_col = 1; - break; - - case '.': - current_col = 3; - break; - - case 'E': case 'e': - current_col = 4; - break; - - default: - if( isdigit( line[lexstart] )) - { - current_col = 2; - } - else if( isdone( line[lexstart] )) - { - current_col = 5; - } - else - { - current_col = 6; - } - break; - } - - next_state = state_table[current_state][current_col]; - - switch( next_state ) - { - case 1: /* - */ - negate = TRUE; - case 2: /* + */ - delimiter = line[lexterm]; /* Move past the + or - character. */ - nextLexBlank(); - break; - - case 3: /* Number (+-ddd) */ - input_value = evalDubl( 0 ); /* Integer part of the number. */ - nextLexeme(); /* Move past previous lexeme. */ - break; - - case 4: - delimiter = line[lexterm]; - nextLexBlank(); /* Move past the . character. */ - break; - - case 5: /* . (+-ddd.ddd) */ - /* Fractional part of the number. */ - input_value = evalDubl( input_value ); - right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ - nextLexeme(); /* Move past previous lexeme. */ - break; - - case 6: /* E (+-ddd.dddE) */ - delimiter = line[lexterm]; /* Move past the E. */ - nextLexBlank(); - break; - - case 7: /* - (+-ddd.dddE-) */ - negate_exponent = TRUE; - case 8: /* + (+-ddd.dddE+) */ - delimiter = line[lexterm]; /* Move past the + or - character. */ - nextLexBlank(); - break; - - case 9: /* # (+-ddd.dddE+-dd) */ - exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ - if( negate_exponent ) { exponent = - exponent; } - nextLexeme(); /* Move past previous lexeme. */ - break; - - case 10: /* Floating number parsed, convert */ - /* the number. */ - /* Get the exponent for the number as input. */ - exponent -= right_digits; - - /* Remove trailing zeros and adjust the exponent accordingly. */ - while(( input_value % 10 ) == 0 ) - { - input_value /= 10; - exponent++; - } - - /* Convert the number to floating point. The number is calculated with */ - /* a 27 bit mantissa to improve precision. The extra 3 bits are */ - /* discarded after the result has been calculated. */ - fltg->exponent = 26; - fltg->mantissa = input_value << 3; - normalizeFltg( fltg ); - - - while( exponent != 0 ) - { - if( exponent < 0 ) - { - /* Decimal point is to the left. */ - fltg->mantissa /= 10; - normalizeFltg( fltg ); - exponent++; - } - else if( exponent > 0 ) - { - /* Decimal point is to the right. */ - fltg->mantissa *= 10; - normalizeFltg( fltg ); - exponent--; - } - } - - /* Discard the extra precsion used for calculating the number. */ - fltg->mantissa >>= 3; - fltg->exponent -= 3; - if( negate ) - { - fltg->mantissa = (- fltg->mantissa ) & 077777777L; - } - return( fltg ); - - case 11: /* Error in format. */ - /* Not a properly constructued floating point number. */ - return( fltg ); - default: - break; - } - /* Set state for next pass through the loop. */ - current_state = next_state; - } -} /* evalFltg() */ - - - -/******************************************************************************/ -/* */ -/* Function: normalizeFltg */ -/* */ -/* Synopsis: Normalize a PDP-8 double precision floating point number. */ -/* */ -/******************************************************************************/ -void normalizeFltg( FLTG_T *fltg ) -{ - /* Normalize the floating point number. */ - if( fltg->mantissa != 0 ) - { - if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) - { - while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) - - { - fltg->mantissa <<= 1; - fltg->exponent--; - } - } - else - { - while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) - { - fltg->mantissa >>= 1; - fltg->exponent++; - } - } - } - else - { - fltg->exponent = 0; - } - return; -} - - -/******************************************************************************/ -/* */ -/* Function: incrementClc */ -/* */ -/* Synopsis: Set the next assembly location. Test for collision with */ -/* the literal tables. */ -/* */ -/******************************************************************************/ -WORD32 incrementClc() -{ - testForLiteralCollision( clc ); - - /* Incrementing the location counter is not to change field setting. */ - clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); - fieldlc = clc & 07777; - return( clc ); -} /* incrementClc() */ - - -/******************************************************************************/ -/* */ -/* Function: testForLiteralCollision */ -/* */ -/* Synopsis: Test the given location for collision with the literal tables. */ -/* */ -/******************************************************************************/ -BOOL testForLiteralCollision( WORD32 loc ) -{ - WORD32 pagelc; - WORD32 pageno; - BOOL result = FALSE; - - pageno = GET_PAGE (loc); - pagelc = loc & 00177; - - if( pageno == 0 ) - { - if( pagelc >= lit_loc[pageno] && !pz.error ) - { - errorMessage( &pz_literal_overflow, -1 ); - pz.error = TRUE; - result = TRUE; - } - } - else - { - if( pagelc >= lit_loc[pageno] && !cp.error ) - { - errorMessage( &literal_overflow, -1 ); - cp.error = TRUE; - result = TRUE; - } - } - return( result ); -} /* testForLiteralCollision() */ - - -/******************************************************************************/ -/* */ -/* Function: readLine */ -/* */ -/* Synopsis: Get next line of input. Print previous line if needed. */ -/* */ -/******************************************************************************/ -void readLine() -{ - BOOL ffseen; - WORD32 ix; - WORD32 iy; - char mc; - char inpline[4*LINELEN]; - - listLine(); /* List previous line if needed. */ - indirect_generated = FALSE; /* Mark no indirect address generated. */ - error_in_line = FALSE; /* No error in line. */ - - if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ - { - mac_ptr = NULL; - for( ix = 0; ix < LINELEN; ix++ ) - { - line[ix] = mac_line[ix]; /* Restore invoking line. */ - } - cc = lexstartprev = mac_cc; /* Restore cc. */ - maxcc = strlen( line ); /* Restore maxcc. */ - listed = TRUE; /* Already listed. */ - return; - } - - cc = 0; /* Initialize column counter. */ - lexstartprev = 0; - if( mac_ptr ) /* Inside macro? */ - { - maxcc = 0; - do - { - mc = *mac_ptr++; /* Next character. */ - if( islower( mc )) /* Encoded argument number? */ - { - ix = mc - 'a'; /* Convert to index. */ - if( iy = mac_arg_pos[ix] ) - { - do /* Copy argument string. */ - { - line[maxcc++] = mac_line[iy++]; - } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && - ( !isend( mac_line[iy] ))); - } - } - else /* Ordinary character, just copy. */ - { - line[maxcc++] = mc; - } - } while( !isend( mc )); - line[maxcc] = '\0'; - listed = nomac_exp; - return; - } - - lineno++; /* Count lines read. */ - listed = FALSE; /* Mark as not listed. */ -READ_LINE: - if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) - { - filix_curr++; /* Advance to next file. */ - if( filix_curr < save_argc ) /* More files? */ - { - fclose( infile ); - if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) - { - fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], - save_argv[filix_curr] ); - exit( -1 ); - } - goto READ_LINE; - } - else - { - inpline[0] = '$'; - inpline[1] = '\n'; - inpline[2] = '\0'; - } - } - - /* Remove any tabs from the input line by inserting the required number */ - /* of spaces to simulate 8 character tab stops. */ - ffseen = FALSE; - for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) - { - switch( inpline[ix] ) - { - case '\t': - do - { - line[iy] = ' '; - iy++; - } - while(( iy % 8 ) != 0 ); - break; - - case '\f': - if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); - ffseen = TRUE; - break; - - default: - line[iy] = inpline[ix]; - iy++; - break; - } - } - line[iy] = '\0'; - - /* If the line is terminated by CR-LF, remove, the CR. */ - if( line[iy - 2] == '\r' ) - { - iy--; - line[iy - 1] = line[iy - 0]; - line[iy] = '\0'; - } - maxcc = iy; /* Save the current line length. */ -} /* readLine() */ - - -/******************************************************************************/ -/* */ -/* Function: listLine */ -/* */ -/* Synopsis: Output a line to the listing file. */ -/* */ -/******************************************************************************/ -void listLine() -/* generate a line of listing if not already done! */ -{ - if( listfile != NULL && listed == FALSE ) - { - printLine( line, 0, 0, LINE ); - } -} /* listLine() */ - - -/******************************************************************************/ -/* */ -/* Function: printPageBreak */ -/* */ -/* Synopsis: Output a Top of Form and listing header if new page necessary. */ -/* */ -/******************************************************************************/ -void printPageBreak() -{ - if( page_lineno >= LIST_LINES_PER_PAGE ) - /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ - { - if( !list_title_set ) - { - strcpy( list_title, line ); - if( list_title[strlen(list_title) - 1] == '\n' ) - { - list_title[strlen(list_title) - 1] = '\0'; - } - if( strlen( list_title ) > TITLELEN ) - { - list_title[TITLELEN] = '\0'; - } - list_title_set = TRUE; - } - topOfForm( list_title, NULL ); - - } -} /* printPageBreak() */ - - -/******************************************************************************/ -/* */ -/* Function: printLine */ -/* */ -/* Synopsis: Output a line to the listing file with new page if necessary. */ -/* */ -/******************************************************************************/ -void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) -{ - if( listfile == NULL ) - { - save_error_count = 0; - return; - } - - printPageBreak(); - - list_lineno++; - page_lineno++; - switch( linestyle ) - { - default: - case LINE: - fprintf( listfile, "%5d ", lineno ); - fputs( line, listfile ); - listed = TRUE; - break; - - case LINE_VAL: - if( !listed ) - { - fprintf( listfile, "%5d %4.4o ", lineno, val ); - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %4.4o\n", val ); - } - break; - - case LINE_LOC_VAL: - if( !listed ) - { - if( indirect_generated && lgm_flag ) - { - fprintf( listfile, "%5d %5.5o %4.4o@ ", lineno, loc, val ); - } - else - { - fprintf( listfile, "%5d %5.5o %4.4o ", lineno, loc, val ); - } - fputs( line, listfile ); - listed = TRUE; - } - else - { - fprintf( listfile, " %5.5o %4.4o\n", loc, val ); - } - break; - - case LOC_VAL: - fprintf( listfile, " %5.5o %4.4o\n", loc, val ); - break; - } - printErrorMessages(); -} /* printLine() */ - - -/******************************************************************************/ -/* */ -/* Function: printErrorMessages */ -/* */ -/* Synopsis: Output any error messages from the current list of errors. */ -/* */ -/******************************************************************************/ -void printErrorMessages() -{ - WORD32 ix; - WORD32 iy; - - if( listfile != NULL ) - { - /* If any errors, display them now. */ - for( iy = 0; iy < save_error_count; iy++ ) - { - printPageBreak(); - fprintf( listfile, "%-18.18s", error_list[iy].mesg ); - if( error_list[iy].col >= 0 ) - { - for( ix = 0; ix < error_list[iy].col; ix++ ) - { - if( line[ix] == '\t' ) - { - putc( '\t', listfile ); - } - else - { - putc( ' ', listfile ); - } - } - fputs( "^", listfile ); - list_lineno++; - page_lineno++; - } - fputs( "\n", listfile ); - } - } - save_error_count = 0; -} /* printErrorMessages() */ - - -/******************************************************************************/ -/* */ -/* Function: endOfBinary */ -/* */ -/* Synopsis: Outputs both literal tables at the end of a binary segment. */ -/* */ -/******************************************************************************/ -void endOfBinary() -{ - /* Points to end of page for () operands. */ - punchLiteralPool( &cp, clc - 1 ); - /* Points to end of page zero for [] operands. */ - punchLiteralPool( &pz, field ); - if( error_in_line ) - listLine(); /* List line if not done yet. */ - return; -} /* endOfBinary() */ - - -/******************************************************************************/ -/* */ -/* Function: punchChecksum */ -/* */ -/* Synopsis: Output a checksum if the current mode requires it and an */ -/* object file exists. */ -/* */ -/******************************************************************************/ -void punchChecksum() -{ - /* If the assembler has output any BIN data output the checksum. */ - if( binary_data_output && !rim_mode ) - { - punchLocObject( 0, checksum ); - } - binary_data_output = FALSE; - checksum = 0; -} /* punchChecksum() */ - - -/******************************************************************************/ -/* */ -/* Function: punchLeader */ -/* */ -/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ -/* documentation. Paper tape has 10 punches per inch. */ -/* */ -/******************************************************************************/ -void punchLeader( WORD32 count ) -{ - WORD32 ix; - - /* If value is zero, set to the default of 2 feet of leader. */ - count = ( count == 0 ) ? 240 : count; - - if( objectfile != NULL ) - { - for( ix = 0; ix < count; ix++ ) - { - fputc( 0200, objectfile ); - } - } -} /* punchLeader() */ - - -/******************************************************************************/ -/* */ -/* Function: punchOrigin */ -/* */ -/* Synopsis: Output an origin to the object file. */ -/* */ -/******************************************************************************/ -void punchOrigin( WORD32 loc ) -{ - punchObject((( loc >> 6 ) & 0077 ) | 0100 ); - punchObject( loc & 0077 ); -} /* punchOrigin() */ - - -/******************************************************************************/ -/* */ -/* Function: punchObject */ -/* */ -/* Synopsis: Put one character to object file and include it in checksum. */ -/* */ -/******************************************************************************/ -void punchObject( WORD32 val ) -{ - val &= 0377; - if( objectfile != NULL ) - { - fputc( val, objectfile ); - checksum += val; - } - binary_data_output = TRUE; -} /* punchObject() */ - - -/******************************************************************************/ -/* */ -/* Function: punchOutObject */ -/* */ -/* Synopsis: Output the current line and then then punch value to the */ -/* object file. */ -/* */ -/******************************************************************************/ -void punchOutObject( WORD32 loc, WORD32 val ) -{ - printLine( line, ( field | loc ), val, LINE_LOC_VAL ); - punchLocObject( loc, val ); -} /* punchOutObject() */ - -/******************************************************************************/ -/* */ -/* Function: punchLocObject */ -/* */ -/* Synopsis: Output the word (with origin if rim format) to the object file.*/ -/* */ -/******************************************************************************/ -void punchLocObject( WORD32 loc, WORD32 val ) -{ - if( rim_mode ) - { - punchOrigin( loc ); - } - punchObject(( val >> 6 ) & 0077 ); - punchObject( val & 0077 ); -} /* punchLocObject() */ - - -/******************************************************************************/ -/* */ -/* Function: punchLiteralPool */ -/* */ -/* Synopsis: Output the current page data. */ -/* */ -/******************************************************************************/ -void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) -{ - WORD32 loc; - WORD32 pageno; - WORD32 tmplc; - - pageno = GET_PAGE (lpool_page); - lpool_page &= 07600; - - if(( lpool_page == 0 ) && ( p != &pz )) - { - return; /* Don't punch page 0 pool if not asked. */ - } - - if( lit_loc[pageno] < lit_base[pageno] ) - { - if( !rim_mode ) - { - /* Put out origin if not in rim mode. */ - punchOrigin( lit_loc[pageno] | lpool_page ); - } - /* Put the literals in the object file. */ - for( loc = lit_loc[pageno]; loc < lit_base[pageno]; loc++ ) - { - tmplc = loc + lpool_page; - printLine( line, (field | tmplc), p->pool[loc], LOC_VAL ); - punchLocObject( tmplc, p->pool[loc] ); - } - p->error = FALSE; - lit_base[pageno] = lit_loc[pageno]; - } -} /* punchLiteralPool() */ - - -/******************************************************************************/ -/* */ -/* Function: insertLiteral */ -/* */ -/* Synopsis: Add a value to the given literal pool if not already in pool. */ -/* Return the location of the value in the pool. */ -/* */ -/******************************************************************************/ -WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ) -{ - WORD32 ix; - WORD32 pageno; - LPOOL_T *p; - - p = pool; - pageno = GET_PAGE (pool_page); - - /* If page zero is the current page, make sure that literals are inserted */ - /* in the page zero literal table. */ - if(( pool_page & 07600 ) == 0 ) - { - p = &pz; - } - - /* Search the literal pool for any occurence of the needed value. */ - ix = lit_base[pageno] - 1; - while( ix >= lit_loc[pageno] && p->pool[ix] != value ) - { - ix--; - } - - /* Check if value found in literal pool. If not, then insert value. */ - if( ix < lit_loc[pageno] ) - { - lit_loc[pageno]--; - p->pool[lit_loc[pageno]] = value; - ix = lit_loc[pageno]; - } - return( ix ); -} /* insertLiteral() */ - -/******************************************************************************/ -/* */ -/* Function: testZeroPool */ -/* */ -/* Synopsis: Test for literal in page zero pool. */ -/* */ -/******************************************************************************/ -BOOL testZeroPool( WORD32 value ) -{ - WORD32 ix; - WORD32 pageno; - - pageno = GET_PAGE( field ); - for ( ix = lit_loc[pageno]; ix < lit_base[pageno]; ix++ ) - { - if( pz.pool[ix] == value ) return TRUE; - } - return FALSE; -} - - -/******************************************************************************/ -/* */ -/* Function: printSymbolTable */ -/* */ -/* Synopsis: Output the symbol table. */ -/* */ -/******************************************************************************/ -void printSymbolTable() -{ - int col; - int cx; - char *fmt; - int ix; - char mark; - int page; - int row; - int symbol_base; - int symbol_lines; - - symbol_base = number_of_fixed_symbols; - - for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) - { - topOfForm( list_title, s_symtable ); - symbol_lines = LIST_LINES_PER_PAGE - page_lineno; - - for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) - { - list_lineno++; - page_lineno++; - fprintf( listfile, "%5d", list_lineno ); - - for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) - { - /* Get index of symbol for the current line and column */ - cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; - cx += symbol_base; - - /* Make sure that there is a symbol to be printed. */ - if( number_of_fixed_symbols <= cx && cx < symbol_top ) - { - switch( symtab[cx].type & LABEL ) - { - case LABEL: - fmt = " %c%-6.6s %5.5o "; - break; - - default: - fmt = " %c%-6.6s %4.4o "; - break; - } - - switch( symtab[cx].type & ( DEFINED | REDEFINED )) - { - case UNDEFINED: - mark = '?'; - break; - - case REDEFINED: - mark = '#'; - break; - - default: - mark = ' '; - break; - } - fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); - ix++; - } - } - fprintf( listfile, "\n" ); - } - } -} /* printSymbolTable() */ - - -/******************************************************************************/ -/* */ -/* Function: printPermanentSymbolTable */ -/* */ -/* Synopsis: Output the permanent symbol table to a file suitable for */ -/* being input after the EXPUNGE pseudo-op. */ -/* */ -/******************************************************************************/ -void printPermanentSymbolTable() -{ - int ix; - FILE *permfile; - char *s_type; - - if(( permfile = fopen( permpathname, "w" )) == NULL ) - { - exit( 2 ); - } - - fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); - fprintf( permfile, " EXPUNGE\n/\n" ); - /* Print the memory reference instructions first. */ - s_type = " "; - for( ix = 0; ix < symbol_top; ix++ ) - { - if( M_MRI( symtab[ix].type )) - { - fprintf( permfile, "%-7s %s=%4.4o\n", - s_type, symtab[ix].name, symtab[ix].val ); - } - } - - s_type = " "; - for( ix = 0; ix < symbol_top; ix++ ) - { - if( M_FIXED( symtab[ix].type )) - { - if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) - { - fprintf( permfile, "%-7s %s=%4.4o\n", - s_type, symtab[ix].name, symtab[ix].val ); - } - } - } - fprintf( permfile, "/\n FIXTAB\n" ); - fclose( permfile ); -} /* printPermanentSymbolTable() */ - - -/******************************************************************************/ -/* */ -/* Function: printCrossReference */ -/* */ -/* Synopsis: Output a cross reference (concordance) for the file being */ -/* assembled. */ -/* */ -/******************************************************************************/ -void printCrossReference() -{ - int ix; - int symbol_base; - int xc; - int xc_index; - int xc_refcount; - int xc_cols; - - /* Force top of form for first page. */ - page_lineno = LIST_LINES_PER_PAGE; - - list_lineno = 0; - symbol_base = number_of_fixed_symbols; - - for( ix = symbol_base; ix < symbol_top; ix++ ) - { - list_lineno++; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - { - topOfForm( list_title, s_xref ); - } - - fprintf( listfile, "%5d", list_lineno ); - - /* Get reference count & index into concordance table for this symbol. */ - xc_refcount = symtab[ix].xref_count; - xc_index = symtab[ix].xref_index; - /* Determine how to label symbol on concordance. */ - switch( symtab[ix].type & ( DEFINED | REDEFINED )) - { - case UNDEFINED: - fprintf( listfile, " U "); - break; - - case REDEFINED: - fprintf( listfile, " M %5d ", xreftab[xc_index] ); - break; - - default: - fprintf( listfile, " A %5d ", xreftab[xc_index] ); - break; - } - fprintf( listfile, "%-6.6s ", symtab[ix].name ); - - /* Output the references, 8 numbers per line after symbol name. */ - for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) - { - if( xc_cols >= XREF_COLUMNS ) - { - xc_cols = 0; - page_lineno++; - if( page_lineno >= LIST_LINES_PER_PAGE ) - { - topOfForm( list_title, s_xref); - } - list_lineno++; - fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); - } - fprintf( listfile, " %5d", xreftab[xc_index + xc] ); - } - fprintf( listfile, "\n" ); - } -} /* printCrossReference() */ - - -/******************************************************************************/ -/* */ -/* Function: topOfForm */ -/* */ -/* Synopsis: Prints title and sub-title on top of next page of listing. */ -/* */ -/******************************************************************************/ -void topOfForm( char *title, char *sub_title ) -{ - char temp[10]; - - if (!listfile) return; - list_pageno++; - strcpy( temp, s_page ); - sprintf( temp, "%s %d", s_page, list_pageno ); - - /* Output a top of form if not the first page of the listing. */ - if( list_pageno > 1 ) - { - fprintf( listfile, "\f" ); - } - fprintf( listfile, "\n %-63s %10s\n", title, temp ); - - /* Reset the current page line counter. */ - page_lineno = 1; - if( sub_title != NULL ) - { - fprintf( listfile, "%80s\n", sub_title ); - page_lineno++; - } - else - { - fprintf( listfile, "\n" ); - page_lineno++; - } - fprintf( listfile, "\n" ); - page_lineno++; -} /* topOfForm() */ - - -/******************************************************************************/ -/* */ -/* Function: lexemeToName */ -/* */ -/* Synopsis: Convert the current lexeme into a string. */ -/* */ -/******************************************************************************/ -char *lexemeToName( char *name, WORD32 from, WORD32 term ) -{ - WORD32 to; - - to = 0; - - while( from < term && to < ( SYMLEN - 1 )) - { - name[to++] = toupper( line[from++] ); - } - - while( to < SYMLEN ) - { - name[to++] = '\0'; - } - return( name ); -} /* lexemeToName() */ - -/******************************************************************************/ -/* */ -/* Function: defineLexeme */ -/* */ -/* Synopsis: Put lexeme into symbol table with a value. */ -/* */ -/******************************************************************************/ -SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ - WORD32 term, /* end+1 of lexeme being defined. */ - WORD32 val, /* value of lexeme being defined. */ - SYMTYP type ) /* how symbol is being defined. */ -{ - char name[SYMLEN]; - - lexemeToName( name, start, term); - return( defineSymbol( name, val, type, start )); -} /* defineLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: defineSymbol */ -/* */ -/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ -/* not already in table. */ -/* */ -/******************************************************************************/ -SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) -{ - SYM_T *sym; - WORD32 xref_count; - - val = val & 07777; - if( strlen( name ) < 1 ) - { - return( &sym_undefined ); /* Protect against non-existent names. */ - } - sym = lookup( name ); - xref_count = 0; /* Set concordance for normal defintion. */ - - if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) - { - if( pass == 2 ) - { - errorSymbol( &redefined_symbol, sym->name, start ); - type = type | REDEFINED; - sym->xref_count++; /* Referenced symbol, count it. */ - xref_count = sym->xref_count; - } - return ( sym ); - } - if( M_FIXED( sym->type )) - { - return ( sym ); - } - - if( pass == 2 && xref ) - { - /* Put the definition line number in the concordance table. */ - /* Defined symbols are not counted as references. */ - xreftab[sym->xref_index] = lineno; - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + xref_count] = lineno; - } - - /* Now set the value and the type. */ - sym->val = val; - sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; - return( sym ); -} /* defineSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: lookup */ -/* */ -/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ -/* table as undefined. Return address of symbol in table. */ -/* */ -/******************************************************************************/ -SYM_T *lookup( char *name ) -{ - int ix; /* Insertion index */ - int lx; /* Left index */ - int rx; /* Right index */ - - /* First search the permanent symbols. */ - lx = 0; - ix = binarySearch( name, lx, number_of_fixed_symbols ); - - /* If symbol not in permanent symbol table. */ - if( ix < 0 ) - { - /* Now try the user symbol table. */ - ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); - - /* If symbol not in user symbol table. */ - if( ix < 0 ) - { - /* Must put symbol in table if index is negative. */ - ix = ~ix; - if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) - { - errorSymbol( &symbol_table_full, name, lexstart ); - exit( 1 ); - } - - for( rx = symbol_top; rx >= ix; rx-- ) - { - symtab[rx + 1] = symtab[rx]; - } - symbol_top++; - - /* Enter the symbol as UNDEFINED with a value of zero. */ - strcpy( symtab[ix].name, name ); - symtab[ix].type = UNDEFINED; - symtab[ix].val = 0; - symtab[ix].xref_count = 0; - if( xref && pass == 2 ) - { - xreftab[symtab[ix].xref_index] = 0; - } - } - } - - return( &symtab[ix] ); /* Return the location of the symbol. */ -} /* lookup() */ - - -/******************************************************************************/ -/* */ -/* Function: binarySearch */ -/* */ -/* Synopsis: Searches the symbol table within the limits given. If the */ -/* symbol is not in the table, it returns the insertion point. */ -/* */ -/******************************************************************************/ -int binarySearch( char *name, int start, int symbol_count ) -{ - int lx; /* Left index */ - int mx; /* Middle index */ - int rx; /* Right index */ - int compare; /* Results of comparison */ - - lx = start; - rx = symbol_count - 1; - while( lx <= rx ) - { - mx = ( lx + rx ) / 2; /* Find center of search area. */ - - compare = strcmp( name, symtab[mx].name ); - - if( compare < 0 ) - { - rx = mx - 1; - } - else if( compare > 0 ) - { - lx = mx + 1; - } - else - { - return( mx ); /* Found a match in symbol table. */ - } - } /* end while */ - return( ~lx ); /* Return insertion point. */ -} /* binarySearch() */ - - -/******************************************************************************/ -/* */ -/* Function: compareSymbols */ -/* */ -/* Synopsis: Used to presort the symbol table when starting assembler. */ -/* */ -/******************************************************************************/ -int compareSymbols( const void *a, const void *b ) -{ - return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); -} /* compareSymbols() */ - -/******************************************************************************/ -/* */ -/* Function: copyMacLine */ -/* */ -/* Synopsis: Used to copy a macro line to the macro buffer. */ -/* */ -/******************************************************************************/ -int copyMacLine( int length, int from, int term, int nargs ) -{ - char name[SYMLEN]; - int ix; - int jx; - int kx; - BOOL bl; - - bl = TRUE; - for( ix = from; ix < term; ix++ ) - { - if( !is_blank( line[ix] )) bl = FALSE; - } - if( bl || ( length < 0 )) return length; - if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; - for( ix = from; ix < term; ) - { - if( nargs && isalpha( line[ix] )) /* Start of symbol? */ - { - for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ - { - if( !isalnum( line[jx] )) break; - } - lexemeToName( name, ix, jx ); /* Make into name. */ - for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ - { - if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) - { - mac_buffer[length++] = 'a' + (char) kx; - for( ix++; ix < jx; ix++ ) - { - mac_buffer[length++] = 'z'; - } - break; - } /* end if strncmp */ - } /* end for kx */ - if( kx >= nargs ) - { - for ( ; ix < jx; ) - { - mac_buffer[length++] = toupper( line[ix++] ); - } - } - } /*end if nargs */ - else - { - mac_buffer[length++] = toupper( line[ix++] ); - } /* end else */ - } /* end for ix */ - mac_buffer[length++] = '\n'; - mac_buffer[length] = 0; - return length; -} - -/******************************************************************************/ -/* */ -/* Function: evalSymbol */ -/* */ -/* Synopsis: Get the pointer for the symbol table entry if exists. */ -/* If symbol doesn't exist, return a pointer to the undefined sym */ -/* */ -/******************************************************************************/ -SYM_T *evalSymbol() -{ - char name[SYMLEN]; - SYM_T *sym; - - sym = lookup( lexemeToName( name, lexstart, lexterm )); - - sym->xref_count++; /* Count the number of references to symbol. */ - - if( xref && pass == 2 ) - { - /* Put the line number in the concordance table. */ - xreftab[sym->xref_index + sym->xref_count] = lineno; - } - - return( sym ); -} /* evalSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: moveToEndOfLine */ -/* */ -/* Synopsis: Move the parser input to the end of the current input line. */ -/* */ -/******************************************************************************/ -void moveToEndOfLine() -{ - while( !isend( line[cc] )) cc++; - lexstart = cc; - lexterm = cc; - lexstartprev = lexstart; -} /* moveToEndOfLine() */ - -/******************************************************************************/ -/* */ -/* Function: nextLexeme */ -/* */ -/* Synopsis: Get the next lexical element from input line. */ -/* */ -/******************************************************************************/ -void nextLexeme() -{ - /* Save start column of previous lexeme for diagnostic messages. */ - lexstartprev = lexstart; - lextermprev = lexterm; - - while( is_blank( line[cc] )) { cc++; } - lexstart = cc; - - if( isalnum( line[cc] )) - { - while( isalnum( line[cc] )) { cc++; } - } - else if( isend( line[cc] )) - { - /* End-of-Line, don't advance cc! */ - } - else - { - switch( line[cc] ) - { - case '"': /* Quoted letter */ - if( cc + 2 < maxcc ) - { - cc++; - cc++; - } - else - { - errorMessage( &no_literal_value, lexstart ); - cc++; - } - break; - - case '/': /* Comment, don't advance cc! */ - break; - - default: /* All other punctuation. */ - cc++; - break; - } - } - lexterm = cc; -} /* nextLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: nextLexBlank */ -/* */ -/* Synopsis: Used to prevent illegal blanks in expressions. */ -/* */ -/******************************************************************************/ -void nextLexBlank() -{ - nextLexeme(); - if( is_blank( delimiter )) - { - errorMessage( &illegal_blank, lexstart - 1 ); - } - delimiter = line[lexterm]; -} /* nextLexBlank() */ - - - -/******************************************************************************/ -/* */ -/* Function: pseudoOperators */ -/* */ -/* Synopsis: Process pseudo-ops (directives). */ -/* */ -/******************************************************************************/ -BOOL pseudoOperators( PSEUDO_T val ) -{ - int count; - int delim; - int index; - int ix; - WORD32 length; - int level; - int lexstartsave; - WORD32 newfield; - WORD32 oldclc; - int pack; - int pageno; - int pos; - int radixprev; - BOOL status; - SYM_T *sym; - WORD32 value; - WORD32 word; - int width; - static int mask_tab[13] = { 00000, - 00001, 00003, 00007, 00017, 00037, 00077, - 00177, 00377, 00777, 01777, 03777, 07777 }; - - status = TRUE; - switch( (PSEUDO_T) val ) - { - case BINPUNCH: - /* If there has been data output and this is a mode switch, set up to */ - /* output data in BIN mode. */ - if( binary_data_output && rim_mode ) - { - for( ix = 0; ix < TOTAL_PAGES; ix++) - { - lit_loc[ix] = lit_base[ix] = 00200; - } - cp.error = FALSE; - pz.error = FALSE; - punchLeader( 8 ); /* Generate a short leader/trailer. */ - checksum = 0; - binary_data_output = FALSE; - } - rim_mode = FALSE; - break; - - case DECIMAL: - radix = 10; - break; - - case DEFINE: - count = 0; - index = 0; - lexstartsave = lexstart; - while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && - ( count < MAC_MAX_ARGS )) - { - if ( !isalpha( line[lexstart] ) && ( index == 0 )) - { - index = lexstart; - } - lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); - nextLexeme(); - } - if( count == 0 ) /* No macro name. */ - { - errorMessage( &no_macro_name, lexstartsave ); - index = 1; - } - else if( index ) /* Bad argument name. */ - { - errorMessage( &bad_dummy_arg, index ); - } - else if( mac_count >= MAC_TABLE_LENGTH ) - { - errorMessage( ¯o_table_full, lexstartsave ); - index = 1; - } - else - { - value = mac_count; - mac_count++; /* Value is entry in mac_bodies. */ - defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); - } - if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) - { - readLine(); - nextLexeme(); - } - if( index ) - { - conditionFalse(); /* On error skip macro body. */ - } - else if( line[lexstart] == '<' ) - { - /* Invariant: line[cc] is the next unexamined character. */ - index = lexstart + 1; - length = 0; - level = 1; - while( level > 0 ) - { - if( isend( line[cc] ) || ( line[cc] == '/' )) - { - length = copyMacLine( length, index, cc, count - 1 ); - readLine(); - index = 0; - } - else - { - switch( line[cc] ) - { - case '>': - level--; - cc++; - break; - - case '<': - level++; - cc++; - break; - - case '$': - level = 0; - cc++; - break; - - default: - cc++; - break; - } /* end switch */ - } /* end if */ - } /* end while */ - length = copyMacLine( length, index, cc - 1, count - 1 ); - if( length < 0 ) - { - errorMessage (¯o_too_long, lexstart ); - } - else if( length == 0 ) - { - mac_bodies[value] = NULL; - } - else - { - mac_bodies[value] = (char *) malloc( length + 1 ); - if( mac_bodies[value] ) - { - strncpy( mac_bodies[value], mac_buffer, length ); - *( mac_bodies[value] + length ) = 0; - } - else - { - errorMessage( &no_virtual_memory, lexstart ); - } - } - nextLexeme(); - } - else - { - errorMessage( <_expected, lexstart ); - } /* end if */ - break; - - case DUBL: - inputDubl(); - break; - - case EJECT: - page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ - status = FALSE; /* This will force reading of next line */ - break; - - case ENPUNCH: - if( pass == 2 ) - { - objectfile = objectsave; - } - break; - - case EXPUNGE: /* Erase symbol table */ - if( pass == 1 ) - { - symtab[0] = sym_undefined; - symbol_top = 0; - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Enter the pseudo-ops into the symbol table. */ - for( ix = 0; ix < DIM( pseudo ); ix++ ) - { - defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); - } - /* Enter I and Z into the symbol table. */ - for( ix = 0; ix < 2; ix++ ) - { - defineSymbol( permanent_symbols[ix].name, - permanent_symbols[ix].val, - permanent_symbols[ix].type | DEFFIX , 0 ); - } - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - } - break; - - case BANK: - case FIELD: - punchLiteralPool( &cp, clc - 1 ); - punchLiteralPool( &pz, field ); - newfield = field >> 12; - lexstartsave = lexstartprev; - if( isdone( line[lexstart] )) - { - newfield += 1; /* Blank FIELD directive. */ - } - else - { - newfield = (getExpr())->val; /* FIELD with argument. */ - } - - if( rim_mode ) - { - errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ - } - else if( newfield > 7 || newfield < 0 ) - { - errorMessage( &illegal_field_value, lexstartprev ); - } - else - { - value = (( newfield & 0007 ) << 3 ) | 00300; - punchObject( value ); - checksum -= value; /* Field punches are not added to checksum. */ - field = newfield << 12; - } - - clc = 0200 | field; - fieldlc = clc & 07777; - - if( !rim_mode ) - { - punchOrigin( clc ); - } - break; - - case FIXTAB: - /* Mark all current symbols as permanent symbols. */ - if( pass == 1) - { - for( ix = 0; ix < symbol_top; ix++ ) - { - symtab[ix].type = ( symtab[ix].type | FIXED ) & ~CONDITION; - if((( symtab[ix].val & 00777 ) == 0 ) && - ( symtab[ix].val <= 05000 ) && - M_DEFINED( symtab[ix].type ) && - !M_PSEUDO( symtab[ix].type ) && - !M_LABEL( symtab[ix].type ) && - !M_MACRO( symtab[ix].type )) - { - symtab[ix].type = symtab[ix].type | MRI; - } - } - number_of_fixed_symbols = symbol_top; - fixed_symbols = &symtab[symbol_top - 1]; - - /* Re-sort the symbol table */ - qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); - } - break; - - case FLTG: - inputFltg(); - /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ - break; - - case IFDEF: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - nextLexeme(); - if( M_DEFINED_CONDITIONALLY( sym->type )) - { - conditionTrue(); - } - else - { - conditionFalse(); - } - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - break; - - case IFNDEF: - if( isalpha( line[lexstart] )) - { - sym = evalSymbol(); - nextLexeme(); - if( M_DEFINED_CONDITIONALLY( sym->type )) - { - conditionFalse(); - } - else - { - conditionTrue(); - } - } - else - { - errorLexeme( &label_syntax, lexstart ); - } - break; - - case IFNZERO: - if( (getExpr())->val == 0 ) - { - conditionFalse(); - } - else - { - conditionTrue(); - } - break; - - case IFZERO: - if( (getExpr())->val == 0 ) - { - conditionTrue(); - } - else - { - conditionFalse(); - } - break; - - case LGM: - lgm_flag = TRUE; - break; - - case LIST: - listfile = listsave; - break; - - case LIT: - if( clc & 07600 ) - { - punchLiteralPool( &cp, clc ); - } - else - { - punchLiteralPool( &pz, field ); - } - if( !rim_mode ) - { - punchOrigin( clc ); - } - break; - - case LITBAS: - if( clc & 07600 ) - { - punchLiteralPool( &cp, clc ); - } - else - { - punchLiteralPool( &pz, field ); - } - if( !rim_mode ) - { - punchOrigin( clc ); - } - pageno = GET_PAGE (clc); - if( isdone( line[lexstart] )) - { - lit_loc[pageno] = lit_base[pageno] = 0200; - } - else - { - lit_loc[pageno] = lit_base[pageno] = (((getExpr())->val) & 0177) + 1; - } - break; - - case NOLGM: - lgm_flag = FALSE; - break; - - case NOPUNCH: - if( pass == 2 ) - { - objectfile = NULL; - } - break; - - case OCTAL: - radix = 8; - break; - - case PAGE: - punchLiteralPool( &cp, clc - 1 ); - oldclc = clc; - if( isdone( line[lexstart] )) - { - clc = ( clc + 0177 ) & 077600; /* No argument. */ - fieldlc = clc & 07777; - } - else - { - value = (getExpr())->val; - clc = field + (( value & 037 ) << 7 ); - fieldlc = clc & 07777; - } - testForLiteralCollision( clc ); - - if( !rim_mode && clc != oldclc ) - { - punchOrigin( clc ); - } - break; - - case PAUSE: - break; - - case RELOC: - if( isdone( line[lexstart] )) - { - reloc = 0; /* Blank RELOC directive. */ - } - else - { - value = (getExpr())->val; /* RELOC with argument. */ - reloc = value - ( clc + reloc ); - } - break; - - case RIMPUNCH: - /* If the assembler has output any BIN data, output the literal tables */ - /* and the checksum for what has been assembled and setup for RIM mode. */ - if( binary_data_output && !rim_mode ) - { - endOfBinary(); - punchChecksum(); - punchLeader( 8 ); /* Generate a short leader/trailer. */ - } - rim_mode = TRUE; - break; - - case TEXT: - delim = line[lexstart]; - pack = 0; - count = 0; - index = lexstart + 1; - while( line[index] != delim && !isend( line[index] )) - { - pack = ( pack << 6 ) | ( line[index] & 077 ); - count++; - if( count > 1 ) - { - punchOutObject( clc, pack ); - incrementClc(); - count = 0; - pack = 0; - } - index++; - } - - if( count != 0 ) - { - punchOutObject( clc, pack << 6 ); - incrementClc(); - } - else - { - punchOutObject( clc, 0 ); - incrementClc(); - } - - if( isend( line[index] )) - { - cc = index; - lexterm = cc; - errorMessage( &text_string, cc ); - } - else - { - cc = index + 1; - lexterm = cc; - } - nextLexeme(); - break; - - case TITLE: - delim = line[lexstart]; - ix = lexstart + 1; - /* Find string delimiter. */ - do - { - if( list_title[ix] == delim && list_title[ix + 1] == delim ) - { - ix++; - } - ix++; - } while( line[ix] != delim && !isend(line[ix]) ); - - if( !isend( line[ix] ) ) - { - count = 0; - ix = lexstart + 1; - do - { - if( list_title[ix] == delim && list_title[ix + 1] == delim ) - { - ix++; - } - list_title[count] = line[ix]; - count++; - ix++; - } while( line[ix] != delim && !isend(line[ix]) ); - - if( strlen( list_title ) > TITLELEN ) - { - list_title[TITLELEN] = '\0'; - } - - cc = ix + 1; - lexterm = cc; - page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ - list_title_set = TRUE; - } - else - { - cc = ix; - lexterm = cc; - errorMessage( &text_string, cc ); - } - - nextLexeme(); - break; - - case UNLIST: - listfile = NULL; - break; - - case VFD: - pos = 0; - word = 0; - radixprev = radix; - while( !isdone (line[lexstart] )) - { - lexstartsave = lexstart; - radix = 10; - width = (getExpr())->val; /* Get field width. */ - radix = radixprev; - if( (width <= 0) || ((width + pos) > 12) || (line[lexstart] != ':') ) - { - errorMessage( &illegal_vfd_value, lexstartsave ); - } - nextLexBlank(); /* Skip colon. */ - value = (getExpr())->val; /* Get field value. */ - if( line[lexterm] == ',' ) cc++; - nextLexeme(); /* Advance to next field. */ - pos = pos + width; - if( pos <= 12 ) - { - word = word | ((value & mask_tab[width]) << (12 - pos)); - } - } - punchOutObject( clc, word ); - incrementClc(); - break; - - case ZBLOCK: - value = (getExpr())->val; - if( value < 0 ) - { - errorMessage( &zblock_too_small, lexstartprev ); - } - else if( value + ( clc & 07777 ) - 1 > 07777 ) - { - errorMessage( &zblock_too_large, lexstartprev ); - } - else - { - for( ; value > 0; value-- ) - { - punchOutObject( clc, 0 ); - incrementClc(); - } - } - - break; - - default: - break; - } /* end switch for pseudo-ops */ - return( status ); -} /* pseudoOperators() */ - - -/******************************************************************************/ -/* */ -/* Function: conditionFalse */ -/* */ -/* Synopsis: Called when a false conditional has been evaluated. */ -/* Lex should be the opening <; ignore all text until */ -/* the closing >. */ -/* */ -/******************************************************************************/ -void conditionFalse() -{ - int level; - - if( line[lexstart] == '<' ) - { - /* Invariant: line[cc] is the next unexamined character. */ - level = 1; - while( level > 0 ) - { - if( isend( line[cc] ) || ( line[cc] == '/' )) - { - readLine(); - } - else - { - switch( line[cc] ) - { - case '>': - level--; - cc++; - break; - - case '<': - level++; - cc++; - break; - - case '$': - level = 0; - cc++; - break; - - default: - cc++; - break; - } /* end switch */ - } /* end if */ - } /* end while */ - nextLexeme(); - } - else - { - errorMessage( <_expected, lexstart ); - } -} /* conditionFalse() */ - -/******************************************************************************/ -/* */ -/* Function: conditionTrue */ -/* */ -/* Synopsis: Called when a true conditional has been evaluated. */ -/* Lex should be the opening <; skip it and setup for */ -/* normal assembly. */ -/* */ -/******************************************************************************/ -void conditionTrue() -{ - if( line[lexstart] == '<' ) - { - nextLexeme(); /* Skip the opening '<' */ - } - else - { - errorMessage( <_expected, lexstart ); - } -} /* conditionTrue() */ - - -/******************************************************************************/ -/* */ -/* Function: errorLexeme */ -/* */ -/* Synopsis: Display an error message using the current lexical element. */ -/* */ -/******************************************************************************/ -void errorLexeme( EMSG_T *mesg, WORD32 col ) -{ - char name[SYMLEN]; - - errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); -} /* errorLexeme() */ - - -/******************************************************************************/ -/* */ -/* Function: errorSymbol */ -/* */ -/* Synopsis: Display an error message with a given string. */ -/* */ -/******************************************************************************/ -void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) -{ - char linecol[12]; - char *s; - - if( pass == 2 ) - { - s = ( name == NULL ) ? "" : name ; - errors++; - sprintf( linecol, "(%d:%d)", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", - filename, linecol, mesg->file, s, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorSymbol() */ - - -/******************************************************************************/ -/* */ -/* Function: errorMessage */ -/* */ -/* Synopsis: Display an error message without a name argument. */ -/* */ -/******************************************************************************/ -void errorMessage( EMSG_T *mesg, WORD32 col ) -{ - char linecol[12]; - - if( pass == 2 ) - { - errors++; - sprintf( linecol, "(%d:%d)", lineno, col + 1 ); - fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", - filename, linecol, mesg->file, clc ); - saveError( mesg->list, col ); - } - error_in_line = TRUE; -} /* errorMessage() */ - -/******************************************************************************/ -/* */ -/* Function: saveError */ -/* */ -/* Synopsis: Save the current error in a list so it may displayed after the */ -/* the current line is printed. */ -/* */ -/******************************************************************************/ -void saveError( char *mesg, WORD32 col ) -{ - if( save_error_count < DIM( error_list )) - { - error_list[save_error_count].mesg = mesg; - error_list[save_error_count].col = col; - save_error_count++; - } - error_in_line = TRUE; - - if( listed ) - { - printErrorMessages(); - } -} /* saveError() */ -/* End-of-File */ +/******************************************************************************/ +/* */ +/* Program: MACRO8X */ +/* File: macro8x.c */ +/* Author: Gary A. Messenbrink */ +/* MACRO8X modifications: Bob Supnik (:) : error: at Loc = */ +/* */ +/* An example error message is: */ +/* */ +/* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ +/* */ +/* The error diagnostics put in the listing start with a two character */ +/* error code (if appropriate) and a short message. A carat '^' is */ +/* placed under the item in error if appropriate. */ +/* An example error message is: */ +/* */ +/* 17 07616 3000 DCA UNDEF */ +/* UD undefined ^ */ +/* 18 07617 1777 TAD I DUMMY */ +/* */ +/* When an indirect is generated, an at character '@' is placed after the */ +/* the instruction value in the listing as an indicator as follows: */ +/* */ +/* 14 03716 1777@ TAD OFFPAG */ +/* */ +/* Undefined symbols are marked in the symbol table listing by prepending */ +/* a '?' to the symbol. Redefined symbols are marked in the symbol table */ +/* listing by prepending a '#' to the symbol. Examples are: */ +/* */ +/* #REDEF 04567 */ +/* SWITCH 07612 */ +/* ?UNDEF 00000 */ +/* */ +/* Refer to the code for the diagnostic messages generated. */ +/* */ +/* BUGS */ +/* Only a minimal effort has been made to keep the listing format */ +/* anything like the PAL-8 listing format. */ +/* The operation of the conditional assembly pseudo-ops may not function */ +/* exactly as the DEC versions. I did not have any examples of these so */ +/* the implementation is my interpretation of how they should work. */ +/* */ +/* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ +/* file type that was specified on startup. This was intentional and */ +/* and allows rim formatted data to be output prior to the actual binary */ +/* formatted data. On UN*X style systems, the same effect can be achieved */ +/* by using the "cat" command, but on DOS/Windows systems, doing this was */ +/* a major chore. */ +/* */ +/* The floating point input does not generate values exactly as the DEC */ +/* compiler does. I worked out several examples by hand and believe that */ +/* this implementation is slightly more accurate. If I am mistaken, */ +/* let me know and, if possible, a better method of generating the values. */ +/* */ +/* BUILD and INSTALLATION */ +/* This program has been built and successfully executed on: */ +/* a. Linux (80486 CPU)using gcc */ +/* b. RS/6000 (AIX 3.2.5) */ +/* c. Borland C++ version 3.1 (large memory model) */ +/* d. Borland C++ version 4.52 (large memory model) */ +/* with no modifications to the source code. */ +/* */ +/* On UNIX type systems, store the the program as the pal command */ +/* and on PC type systems, store it as pal.exe */ +/* */ +/* HISTORICAL NOTE: */ +/* This assembler was written to support the fleet of PDP-8 systems */ +/* used by the Bay Area Rapid Transit System. As of early 1997, */ +/* this includes about 40 PDP-8/E systems driving the train destination */ +/* signs in passenger stations. */ +/* */ +/* REFERENCES: */ +/* This assembler is based on the pal assember by: */ +/* Douglas Jones and */ +/* Rich Coon */ +/* */ +/* DISCLAIMER: */ +/* See the symbol table for the set of pseudo-ops supported. */ +/* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ +/* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ +/* documentation of pseudo-ops. */ +/* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ +/* lower level introduction to the assembly language. */ +/* */ +/* WARRANTY: */ +/* If you don't like it the way it works or if it doesn't work, that's */ +/* tough. You're welcome to fix it yourself. That's what you get for */ +/* using free software. */ +/* */ +/* COPYRIGHT NOTICE: */ +/* This is free software. There is no fee for using it. You may make */ +/* any changes that you wish and also give it away. If you can make */ +/* a commercial product out of it, fine, but do not put any limits on */ +/* the purchaser's right to do the same. If you improve it or fix any */ +/* bugs, it would be nice if you told me and offered me a copy of the */ +/* new version. */ +/* */ +/* */ +/* Amendments Record: */ +/* Version Date by Comments */ +/* ------- ------- --- --------------------------------------------------- */ +/* v1.0 12Apr96 GAM Original */ +/* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ +/* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ +/* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ +/* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ +/* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ +/* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ +/* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ +/* v3.0 14Feb97 RMS MACRO8X features. */ +/* v3.1 16Sep01 RMS Bug fixes: */ +/* - IFNZRO instead of IFNZERO */ +/* - allow blanks after symbol= */ +/* - remove field from label values */ +/* - don't include NOPUNCH data in checksum */ +/* */ +/******************************************************************************/ + +#include +#include +#include +#include + +#define LINELEN 96 +#define LIST_LINES_PER_PAGE 60 /* Includes 5 line page header. */ +#define NAMELEN 128 +#define SYMBOL_COLUMNS 5 +#define SYMLEN 7 +#define SYMBOL_TABLE_SIZE 8192 +#define MAC_MAX_ARGS 20 /* Must be < 26 */ +#define MAC_MAX_LENGTH 8192 +#define MAC_TABLE_LENGTH 1024 /* Must be <= 4096. */ +#define TITLELEN 63 +#define XREF_COLUMNS 8 + +#define ADDRESS_FIELD 00177 +#define FIELD_FIELD 070000 +#define INDIRECT_BIT 00400 +#define LAST_PAGE_LOC 00177 +#define OP_CODE 07000 +#define PAGE_BIT 00200 + +#ifdef PAGE_SIZE +#undef PAGE_SIZE +#endif +#define PAGE_SIZE 00200 + +#define PAGE_FIELD 07600 +#define PAGE_ZERO_END 00200 +#define TOTAL_PAGES (32 * 8) +#define GET_PAGE(x) (((x) >> 7) & (TOTAL_PAGES - 1)) + +/* Macro to get the number of elements in an array. */ +#define DIM(a) (sizeof(a)/sizeof(a[0])) + +/* Macro to get the address plus one of the end of an array. */ +#define BEYOND(a) ((a) + DIM(A)) + +#define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) +#define isend(c) ((c=='\0')|| (c=='\n')) +#define isdone(c) ((c=='/') || (isend(c)) || (c==';')) + +/* Macros for testing symbol attributes. Each macro evaluates to non-zero */ +/* (true) if the stated condtion is met. */ +/* Use these to test attributes. The proper bits are extracted and then */ +/* tested. */ +#define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) +#define M_DEFINED(s) ((s & DEFINED) == DEFINED) +#define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) +#define M_FIXED(s) ((s & FIXED) == FIXED) +#define M_LABEL(s) ((s & LABEL) == LABEL) +#define M_MRI(s) ((s & MRI) == MRI) +#define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) +#define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) +#define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) +#define M_MACRO(s) ((s & MACRO) == MACRO) +#define M_UNDEFINED(s) (!M_DEFINED(s)) +#define M_NOTRDEF(s) ((s & NOTRDEF) != 0) + +/* This macro is used to test symbols by the conditional assembly pseudo-ops. */ +#define M_DEF(s) (M_DEFINED(s)) +#define M_COND(s) (M_CONDITIONAL(s)) +#define M_DEFINED_CONDITIONALLY(t) ((M_DEF(t)&&pass==1)||(!M_COND(t)&&pass==2)) + +typedef unsigned char BOOL; +typedef unsigned char BYTE; +typedef int WORD32; + +#ifndef FALSE + #define FALSE 0 + #define TRUE (!FALSE) +#endif + +/* Line listing styles. Used to control listing of lines. */ +enum linestyle_t +{ + LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL +}; +typedef enum linestyle_t LINESTYLE_T; + +/* Symbol Types. */ +/* Note that the names that have FIX as the suffix contain the FIXED bit */ +/* included in the value. */ +/* */ +/* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ +/* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ +/* defined or undefined. The condition bit is set when the symbol is defined */ +/* during pass 1 and reset on pass 2 at the location the symbol was defined */ +/* during pass 1. When processing conditionals during pass 2, if the symbol */ +/* is defined and the condition bit is set, the symbol is treated as if it */ +/* were undefined. This gives consistent behavior of the conditional */ +/* pseudo-ops during both pass 1 and pass 2. */ +enum symtyp +{ + UNDEFINED = 0000, + DEFINED = 0001, + FIXED = 0002, + MRI = 0004 | DEFINED, + LABEL = 0010 | DEFINED, + REDEFINED = 0020 | DEFINED, + DUPLICATE = 0040 | DEFINED, + PSEUDO = 0100 | FIXED | DEFINED, + CONDITION = 0200, + MACRO = 0400 | DEFINED, + MRIFIX = MRI | FIXED | DEFINED, + DEFFIX = DEFINED | FIXED, + NOTRDEF = (MACRO | PSEUDO | LABEL | MRI | FIXED) & ~DEFINED +}; +typedef enum symtyp SYMTYP; + +enum pseudo_t +{ + BANK, BINPUNCH, DECIMAL, DEFINE, DUBL, EJECT, ENPUNCH, + EXPUNGE, FIELD, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, + IFZERO, LGM, LIST, LIT, LITBAS, NOLGM, NOPUNCH, + OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, TEXT, TITLE, + UNLIST, VFD, ZBLOCK +}; +typedef enum pseudo_t PSEUDO_T; + +struct sym_t +{ + SYMTYP type; + char name[SYMLEN]; + WORD32 val; + WORD32 xref_index; + WORD32 xref_count; +}; +typedef struct sym_t SYM_T; + +struct lpool_t +{ + WORD32 error; /* True if error message has been printed. */ + WORD32 pool[PAGE_SIZE]; +}; +typedef struct lpool_t LPOOL_T; + +struct emsg_t +{ + char *list; + char *file; +}; +typedef struct emsg_t EMSG_T; + +struct errsave_t +{ + char *mesg; + WORD32 col; +}; +typedef struct errsave_t ERRSAVE_T; + +struct fltg_ +{ + WORD32 exponent; + WORD32 mantissa; +}; +typedef struct fltg_ FLTG_T; + +/*----------------------------------------------------------------------------*/ + +/* Function Prototypes */ + +int binarySearch( char *name, int start, int symbol_count ); +int copyMacLine( int length, int from, int term, int nargs ); +int compareSymbols( const void *a, const void *b ); +void conditionFalse( void ); +void conditionTrue( void ); +SYM_T *defineLexeme( WORD32 start, WORD32 term, WORD32 val, SYMTYP type ); +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start); +void endOfBinary( void ); +void errorLexeme( EMSG_T *mesg, WORD32 col ); +void errorMessage( EMSG_T *mesg, WORD32 col ); +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ); +SYM_T *eval( void ); +WORD32 evalDubl( WORD32 initial_value ); +FLTG_T *evalFltg( void ); +SYM_T *evalSymbol( void ); +void getArgs( int argc, char *argv[] ); +WORD32 getDublExpr( void ); +WORD32 getDublExprs( void ); +FLTG_T *getFltgExpr( void ); +FLTG_T *getFltgExprs( void ); +SYM_T *getExpr( void ); +WORD32 getExprs( void ); +WORD32 incrementClc( void ); +void inputDubl( void ); +void inputFltg( void ); +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ); +char *lexemeToName( char *name, WORD32 from, WORD32 term ); +void listLine( void ); +SYM_T *lookup( char *name ); +void moveToEndOfLine( void ); +void nextLexBlank( void ); +void nextLexeme( void ); +void normalizeFltg( FLTG_T *fltg ); +void onePass( void ); +void printCrossReference( void ); +void printErrorMessages( void ); +void printLine(char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle); +void printPageBreak( void ); +void printPermanentSymbolTable( void ); +void printSymbolTable( void ); +BOOL pseudoOperators( PSEUDO_T val ); +void punchChecksum( void ); +void punchLocObject( WORD32 loc, WORD32 val ); +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ); +void punchOutObject( WORD32 loc, WORD32 val ); +void punchLeader( WORD32 count ); +void punchObject( WORD32 val ); +void punchOrigin( WORD32 loc ); +void readLine( void ); +void saveError( char *mesg, WORD32 cc ); +BOOL testForLiteralCollision( WORD32 loc ); +BOOL testZeroPool( WORD32 value ); +void topOfForm( char *title, char *sub_title ); + +/*----------------------------------------------------------------------------*/ + +/* Table of pseudo-ops (directives) which are used to setup the symbol */ +/* table on startup and when the EXPUNGE pseudo-op is executed. */ +SYM_T pseudo[] = +{ + { PSEUDO, "BANK", BANK }, /* Synonym for field in MACRO8X. */ + { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ + { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ + { PSEUDO, "DEFINE", DEFINE }, /* Define macro. */ + { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ + { PSEUDO, "tEJECT", EJECT }, /* Eject a page in the listing. DISABLED */ + { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ + { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ + { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ + { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ + { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ + { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ + { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ + { PSEUDO, "IFNZRO", IFNZERO }, /* Assemble if symbol value is not 0. */ + { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ + { PSEUDO, "LGM", LGM }, /* Enable link generation messages. */ + { PSEUDO, "LIST", LIST }, /* Enable listing. */ + { PSEUDO, "LIT", LIT }, /* Punch literal pool. */ + { PSEUDO, "LITBAS", LITBAS }, /* Set literal pool base. */ + { PSEUDO, "NOLGM", NOLGM }, /* Disable link generation messages. */ + { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ + { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ + { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ + { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ + { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ + { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ + { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ + { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ + { PSEUDO, "UNLIST", UNLIST }, /* Disable listing. */ + { PSEUDO, "VFD", VFD }, /* Variable field definition. */ + { PSEUDO, "ZBLOCK", ZBLOCK } /* Zero a block of memory. */ +}; + +/* Symbol Table */ +/* The table is put in lexical order on startup, so symbols can be */ +/* inserted as desired into the initial table. */ +SYM_T permanent_symbols[] = +{ + /* Memory Reference Instructions */ + { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ + { MRIFIX, "Z", 00000 }, /* PAGE ZERO ADDRESS */ + { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ + { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ + { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ + { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ + { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ + { MRIFIX, "JMP", 05000 }, /* JUMP */ + /* Floating Point Interpreter Instructions */ + { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ + { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ + { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ + { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ + { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ + { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ + { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ + { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ + { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ + { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ + { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ + /* Group 1 Operate Microinstrcutions */ + { FIXED, "OPR", 07000 }, /* OPERATE */ + { FIXED, "NOP", 07000 }, /* NO OPERATION */ + { FIXED, "IAC", 07001 }, /* INCREMENT AC */ + { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ + { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ + { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ + { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ + { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ + { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ + { FIXED, "CLL", 07100 }, /* CLEAR LINK */ + { FIXED, "CLA", 07200 }, /* CLEAR AC */ + /* Group 2 Operate Microinstructions */ + { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ + { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ + { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ + { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ + { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ + { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ + { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ + { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ + { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ + { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ + /* Combined Operate Microinstructions */ + { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ + { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ + { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ + { FIXED, "STA", 07240 }, /* SET AC TO -1 */ + { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ + /* MQ Instructions (PDP/8e) */ + { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ + { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ + /* Program Interrupt */ + { FIXED, "IOT", 06000 }, + { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ + { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ + /* Program Interrupt, PDP-8/e */ + { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ + { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ + { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ + { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ + { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ + { FIXED, "CAF", 06007 }, /* Clear all flags. */ + /* Keyboard/Reader */ + { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ + { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG */ + { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ + { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ + /* Teleprinter/Punch */ + { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ + { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ + { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ + { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ + /* High Speed Paper Tape Reader */ + { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ + { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ + { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ + /* PC8-E High Speed Paper Tape Reader & Punch */ + { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ + { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ + { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ + /* and fetch character. */ + /* High Speed Paper Tape Punch */ + { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ + { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ + { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ + { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ + /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ + { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ + /* into AC bits 0-9 */ + { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ + /* undisturbed */ + { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ + /* OR from AC. If AC bit 10=0, clear */ + /* error flags; if AC bit 11=0, DECtape */ + /* control flag is cleared. */ + { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ + { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ + /* control flag is 1 */ + { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ + /* ORed into AC */ + { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ + /* register B loaded from AC bits 6-8 */ + /* Disk File and Control, Type DF32 */ + { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ + /* INTERRUPT FLAGS */ + { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ + /* INTO CORE, CLEAR INTERRUPT FLAG */ + { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ + /* FROM CORE, CLEAR INTERRUPT FLAG */ + { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ + { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ + /* MEMORY ADDRESS EXTENSION REGISTER */ + { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ + /* MEMORY ADDRESS EXTENSION REGISTER */ + /* AND LOAD SAME FROM AC */ + { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ + /* ADDRESS REGISTER, SKIP IF ADDRESS */ + /* CONFIRMED FLAG = 1 */ + { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ + /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ + /* (NO ERROR) */ + { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ + /* TRANSFER COMPLETE) */ + { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ + /* ADDRESS REGISTER */ + /* Disk File and Control, Type RF08 */ + { FIXED, "DCIM", 06611 }, + { FIXED, "DIML", 06615 }, + { FIXED, "DIMA", 06616 }, + /*{ FIXED, "DISK", 06623 },*/ + { FIXED, "DCXA", 06641 }, + { FIXED, "DXAL", 06643 }, + { FIXED, "DXAC", 06645 }, + { FIXED, "DMMT", 06646 }, + /* Memory Extension Control, Type 183 */ + { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ + { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ + { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ + { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ + { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ + { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ + { FIXED, "RMF", 06244 }, /* RESTORE MEMORY FIELD */ + /* Memory Parity, Type MP8/I (MP8/L) */ + { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ + { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ + /* Memory Parity, Type MP8-E (PDP8/e) */ + { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ + { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ + { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ + { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ + { FIXED, "CEP", 06106 }, /* Check for even parity. */ + { FIXED, "SPO", 06107 }, /* Skip on parity option. */ +}; /* End-of-Symbols for Permanent Symbol Table */ + +/* Global variables */ +SYM_T *symtab; /* Symbol Table */ +int symbol_top; /* Number of entries in symbol table. */ + +SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ +int number_of_fixed_symbols; + +/*----------------------------------------------------------------------------*/ + +WORD32 *xreftab; /* Start of the concordance table. */ + +ERRSAVE_T error_list[20]; +int save_error_count; + +LPOOL_T pz; /* Storage for page zero constants. */ +LPOOL_T cp; /* Storage for current page constants. */ +WORD32 lit_base[TOTAL_PAGES]; /* Literal base address for all pages. */ +WORD32 lit_loc[TOTAL_PAGES]; /* Literal current location for all pages. */ + +char s_detected[] = "detected"; +char s_error[] = "error"; +char s_errors[] = "errors"; +char s_no[] = "No"; +char s_page[] = "Page"; +char s_symtable[] = "Symbol Table"; +char s_xref[] = "Cross Reference"; + +/* Assembler diagnostic messages. */ +/* Some attempt has been made to keep continuity with the PAL-III and */ +/* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ +/* exists, then the indicator is put in the listing as the first two */ +/* characters of the diagnostic message. The PAL-III indicators where used */ +/* when there was a choice between using MACRO-8 and PAL-III indicators. */ +/* The character pairs and their meanings are: */ +/* DT Duplicate Tag (symbol) */ +/* IC Illegal Character */ +/* ID Illegal Redefinition of a symbol. An attempt was made to give */ +/* a symbol a new value not via =. */ +/* IE Illegal Equals An equal sign was used in the wrong context, */ +/* (e.g., A+B=C, or TAD A+=B) */ +/* II Illegal Indirect An off page reference was made, but a literal */ +/* could not be generated because the indirect bit was already set. */ +/* IR Illegal Reference (address is not on current page or page zero) */ +/* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ +/* RD ReDefintion of a symbol */ +/* ST Symbol Table full */ +/* UA Undefined Address (undefined symbol) */ +/* ZE Zero Page Exceeded (see above, or out of space) */ +EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; +EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; +EMSG_T illegal_character = { "IC illegal char", "illegal character" }; +EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; +EMSG_T label_syntax = { "IC label syntax", "label syntax" }; +EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; +EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; +EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; +EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; +EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; +EMSG_T illegal_reference = { "IR off page", "illegal reference" }; +EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; +EMSG_T misplaced_symbol = { "misplaced symbol", "misplaced symbol" }; +EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; +EMSG_T literal_overflow = { "PE page exceeded", + "current page literal capacity exceeded" }; +EMSG_T pz_literal_overflow = { "ZE page exceeded", + "page zero capacity exceeded" }; +EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; +EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; +EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; +EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; +EMSG_T no_pseudo_op = { "not implemented", "Unimplemented pseudo-op" }; +EMSG_T illegal_field_value = { "expr out of range", + "field value not in range of 0 through 7" }; +EMSG_T illegal_vfd_value = { "width out of range", + "VFD field width not in range" }; +EMSG_T no_literal_value = { "no value", "No literal value" }; +EMSG_T text_string = { "no delimiter", + "Text string delimiters not matched" }; +EMSG_T in_rim_mode = { "not OK in rim mode" + "FIELD pseudo-op not valid in RIM mode" }; +EMSG_T lt_expected = { "'<' expected", "'<' expected" }; +EMSG_T symbol_table_full = { "ST Symbol Tbl full", "Symbol table full" }; +EMSG_T no_macro_name = { "no macro name", "No name following DEFINE" }; +EMSG_T bad_dummy_arg = { "bad dummy arg", + "Bad dummy argument following DEFINE" }; +EMSG_T macro_too_long = { "macro too long", "Macro too long" }; +EMSG_T no_virtual_memory = { "out of memory", + "Insufficient memory for macro" }; +EMSG_T macro_table_full = { "Macro Table full", "Macro table full" }; + +/*----------------------------------------------------------------------------*/ + +FILE *errorfile; +FILE *infile; +FILE *listfile; +FILE *listsave; +FILE *objectfile; +FILE *objectsave; + +char errorpathname[NAMELEN]; +char filename[NAMELEN]; +char listpathname[NAMELEN]; +char objectpathname[NAMELEN]; +char *pathname; +char permpathname[NAMELEN]; + +char mac_buffer[MAC_MAX_LENGTH + 1]; +char *mac_bodies[MAC_TABLE_LENGTH]; +char mac_arg_name[MAC_MAX_ARGS][SYMLEN]; +int mac_arg_pos[26] = { 0 }; + +int list_lineno; +int list_pageno; +char list_title[4*LINELEN]; +BOOL list_title_set; /* Set if TITLE pseudo-op used. */ +char line[4*LINELEN]; /* Input line. */ +int lineno; /* Current line number. */ +char mac_line[4*LINELEN]; /* Saved macro invocation line. */ +int page_lineno; /* print line number on current page. */ +WORD32 listed; /* Listed flag. */ +WORD32 listedsave; + +WORD32 cc; /* Column Counter (char position in line). */ +WORD32 checksum; /* Generated checksum */ +BOOL binary_data_output; /* Set true when data has been output. */ +WORD32 clc; /* Location counter */ +char delimiter; /* Character immediately after eval'd term. */ +int errors; /* Number of errors found so far. */ +BOOL error_in_line; /* TRUE if error on current line. */ +int errors_pass_1; /* Number of errors on pass 1. */ +WORD32 field; /* Current field */ +WORD32 fieldlc; /* location counter without field portion. */ +int filix_curr; /* Index in argv to current input file. */ +int filix_start; /* Start of input files in argv. */ +BOOL fltg_input; /* TRUE when doing floating point input. */ +BOOL indirect_generated; /* TRUE if an off page address generated. */ +WORD32 lexstartprev; /* Where previous lexeme started. */ +WORD32 lextermprev; /* Where previous lexeme ended. */ +WORD32 lexstart; /* Index of current lexeme on line. */ +WORD32 lexterm; /* Index of character after current lexeme. */ +WORD32 mac_cc; /* Saved cc after macro invocation. */ +WORD32 mac_count; /* Total macros defined. */ +BOOL nomac_exp; /* Print macro expansions. */ +char *mac_ptr; /* Pointer to macro body, NULL if no macro. */ +WORD32 maxcc; /* Current line length. */ +BOOL lgm_flag; /* Link generated messages enable flag. */ +BOOL overflow; /* Overflow flag for math routines. */ +WORD32 pass; /* Number of current pass. */ +BOOL print_permanent_symbols; +WORD32 radix; /* Default number radix. */ +WORD32 reloc; /* The relocation distance. */ +BOOL rim_mode; /* Generate rim format, defaults to bin */ +int save_argc; /* Saved argc. */ +char **save_argv; /* Saved *argv[]. */ +BOOL symtab_print; /* Print symbol table flag */ +BOOL xref; + +FLTG_T fltg_ac; /* Value holder for evalFltg() */ +SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ +SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ +SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ + + +/******************************************************************************/ +/* */ +/* Function: main */ +/* */ +/* Synopsis: Starting point. Controls order of assembly. */ +/* */ +/******************************************************************************/ +int main( int argc, char *argv[] ) +{ + int ix; + int space; + + save_argc = argc; + save_argv = argv; + + /* Set the default values for global symbols. */ + binary_data_output = FALSE; + fltg_input = FALSE; + nomac_exp = TRUE; + print_permanent_symbols = FALSE; + rim_mode = FALSE; + symtab_print = FALSE; + xref = FALSE; + pathname = NULL; + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++ ) + { + mac_bodies[ix] = NULL; + } + + /* Get the options and pathnames */ + getArgs( argc, argv ); + + /* Setup the error file in case symbol table overflows while installing the */ + /* permanent symbols. */ + errorfile = fopen( errorpathname, "w" ); + errors = 0; + save_error_count = 0; + pass = 0; /* This is required for symbol table initialization. */ + symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); + + if( symtab == NULL ) + { + fprintf( stderr, "Could not allocate memory for symbol table.\n"); + exit( -1 ); + } + + /* Place end marker in symbol table. */ + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + + /* Enter the predefined symbols into the table. */ + /* Also make them part of the permanent symbol table. */ + for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type | DEFFIX , 0 ); + } + + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Do pass one of the assembly */ + checksum = 0; + pass = 1; + onePass(); + errors_pass_1 = errors; + fclose ( errorfile ); + + /* Set up for pass two */ + errorfile = fopen( errorpathname, "w" ); + objectfile = fopen( objectpathname, "wb" ); + objectsave = objectfile; + + listfile = fopen( listpathname, "w" ); + listsave = listfile; + + punchLeader( 0 ); + checksum = 0; + + /* Do pass two of the assembly */ + errors = 0; + save_error_count = 0; + + if( xref ) + { + /* Get the amount of space that will be required for the concordance. */ + for( space = 0, ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].xref_index = space; /* Index into concordance table. */ + space += symtab[ix].xref_count + 1; + symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ + + } + /* Allocate the necessary space. */ + xreftab = (WORD32 *) malloc( sizeof( WORD32 ) * space ); + + /* Clear the cross reference space. */ + for( ix = 0; ix < space; ix++ ) + { + xreftab[ix] = 0; + } + } + pass = 2; + onePass(); + + /* Undo effects of NOPUNCH for any following checksum */ + objectfile = objectsave; + punchChecksum(); + + /* Works great for trailer. */ + punchLeader( 1 ); + + /* undo effects of NOLIST for any following output to listing file. */ + listfile = listsave; + + /* Display value of error counter. */ + if( errors == 0 ) + { + fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); + } + else + { + fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( listfile, "\n %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + fprintf( stderr, " %d %s %s\n", errors, s_detected, + ( errors == 1 ? s_error : s_errors )); + } + + if( symtab_print ) + { + printSymbolTable(); + } + + if( print_permanent_symbols ) + { + printPermanentSymbolTable(); + } + + if( xref ) + { + printCrossReference(); + } + + fclose( objectfile ); + fclose( listfile ); + fclose( errorfile ); + if( errors == 0 && errors_pass_1 == 0 ) + { + remove( errorpathname ); + } + + return( errors != 0 ); +} /* main() */ + +/******************************************************************************/ +/* */ +/* Function: getArgs */ +/* */ +/* Synopsis: Parse command line, set flags accordingly and setup input and */ +/* output files. */ +/* */ +/******************************************************************************/ +void getArgs( int argc, char *argv[] ) +{ + WORD32 len; + WORD32 ix, jx; + + /* Set the defaults */ + errorfile = NULL; + infile = NULL; + listfile = NULL; + listsave = NULL; + objectfile = NULL; + objectsave = NULL; + + for( ix = 1; ix < argc; ix++ ) + { + if( argv[ix][0] == '-' ) + { + for( jx = 1; argv[ix][jx] != 0; jx++ ) + { + switch( argv[ix][jx] ) + { + case 'd': + symtab_print = TRUE; + break; + + case 'm': + nomac_exp = FALSE; + break; + + case 'r': + rim_mode = TRUE; + break; + + case 'p': + print_permanent_symbols = TRUE; + break; + + case 'x': + xref = TRUE; + break; + + default: + fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); + fprintf( stderr, " -d -- dump symbol table\n" ); + fprintf( stderr, " -m -- print macro expansions\n" ); + fprintf( stderr, " -r -- output rim format file\n" ); + fprintf( stderr, " -p -- output permanent symbols to file\n" ); + fprintf( stderr, " -x -- output cross reference to file\n" ); + fflush( stderr ); + exit( -1 ); + } /* end switch */ + } /* end for */ + } + else + { + filix_start = ix; + pathname = argv[ix]; + break; + } + } /* end for */ + + if( pathname == NULL ) + { + fprintf( stderr, "%s: no input file specified\n", argv[0] ); + exit( -1 ); + } + + len = strlen( pathname ); + if( len > NAMELEN - 5 ) + { + fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); + exit( -1 ); + } + + /* Now make the pathnames */ + /* Find last '.', if it exists. */ + jx = len - 1; + while( pathname[jx] != '.' && pathname[jx] != '/' + && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + + switch( pathname[jx] ) + { + case '.': + break; + + case '/': + case '\\': + jx = len; + break; + + default: + break; + } + + /* Add the pathname extensions. */ + strncpy( objectpathname, pathname, jx ); + objectpathname[jx] = '\0'; + strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); + + strncpy( listpathname, pathname, jx ); + listpathname[jx] = '\0'; + strcat( listpathname, ".lst" ); + + strncpy( errorpathname, pathname, jx ); + errorpathname[jx] = '\0'; + strcat( errorpathname, ".err" ); + + strncpy( permpathname, pathname, jx ); + permpathname[jx] = '\0'; + strcat( permpathname, ".prm" ); + + /* Extract the filename from the path. */ + if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) + { + pathname[1] = '\\'; /* MS-DOS style pathname */ + } + + jx = len - 1; + while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) + { + jx--; + } + strcpy( filename, &pathname[jx + 1] ); + +} /* getArgs() */ + + +/******************************************************************************/ +/* */ +/* Function: onePass */ +/* */ +/* Synopsis: Do one assembly pass. */ +/* */ +/******************************************************************************/ +void onePass() +{ + BOOL blanks; + int ix; + int jx; + char name[SYMLEN]; + WORD32 newclc; + BOOL scanning_line; + WORD32 start; + SYM_T *sym; + WORD32 term; + WORD32 val; + + clc = 0200; /* Default starting address is 200 octal. */ + field = 0; + fieldlc = 0200; + reloc = 0; + for( ix = 0; ix < TOTAL_PAGES; ix++ ) + { + lit_loc[ix] = lit_base[ix] = 00200; + } + mac_count = 0; /* No macros defined. */ + mac_ptr = NULL; /* Not in a macro. */ + for( ix = 0; ix < MAC_TABLE_LENGTH; ix++) + { + if ( mac_bodies[ix] ) + { + free( mac_bodies[ix] ); + } + } + cp.error = FALSE; + pz.error = FALSE; + listed = TRUE; + lgm_flag = TRUE; + lineno = 0; + list_pageno = 0; + list_lineno = 0; + list_title_set = FALSE; + page_lineno = LIST_LINES_PER_PAGE; /* Force top of page for new titles. */ + radix = 8; /* Initial radix is octal (base 8). */ + + /* Now open the first input file. */ + filix_curr = filix_start; /* Initialize pointer to input files. */ + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], save_argv[filix_curr] ); + exit( -1 ); + } + + while( TRUE ) + { + readLine(); + nextLexeme(); + + scanning_line = TRUE; + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '$': + endOfBinary(); + fclose( infile ); + return; + + case '*': + nextLexeme(); /* Skip '*', (set origin symbol) */ + newclc = ((getExpr())->val & 07777 ) | field; + /* Do not change Current Location Counter if an error occurred. */ + if( !error_in_line ) + { + if(( newclc & 07600 ) != ( clc & 07600 )) + { + /* Current page has changed. */ + punchLiteralPool( &cp, clc - 1 ); + } + clc = newclc - reloc; + fieldlc = clc & 07777; + + if( !rim_mode ) + { + /* Not rim mode, put out origin. */ + punchOrigin( clc ); + } + printLine( line, 0, fieldlc, LINE_VAL ); + } + break; + + default: + switch( line[lexterm] ) + { + case ',': + if( isalpha( line[lexstart] )) + { + /* Use lookup so symbol will not be counted as reference. */ + sym = lookup( lexemeToName( name, lexstart, lexterm )); + if( M_DEFINED( sym->type )) + { + if( sym->val != (clc & 07777) && pass == 2 ) + { + errorSymbol( &duplicate_label, sym->name, lexstart ); + } + sym->type = sym->type | DUPLICATE; + } + /* Must call define on pass 2 to generate concordance. */ + defineLexeme( lexstart, lexterm, ( clc+reloc ), LABEL ); + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + nextLexeme(); /* skip label */ + nextLexeme(); /* skip comma */ + break; + + case '=': + if( isalpha( line[lexstart] )) + { + start = lexstart; + term = lexterm; + delimiter = line[lexterm]; + nextLexBlank(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + val = getExprs(); + defineLexeme( start, term, val, DEFINED ); + printLine( line, 0, val, LINE_VAL ); + } + else + { + errorLexeme( &symbol_syntax, lexstartprev ); + nextLexeme(); /* skip symbol */ + nextLexeme(); /* skip trailing = */ + getExprs(); /* skip expression */ + } + break; + + default: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + val = sym->val; + if( M_MACRO( sym->type )) + { /* Find arguments. */ + blanks = TRUE; /* Expecting blanks. */ + for( jx = 0; !isdone( line[cc] ) && ( jx < MAC_MAX_ARGS ); cc++ ) + { + if(( line[cc] == ',' ) || is_blank( line[cc] )) blanks = TRUE; + else if( blanks ) + { + mac_arg_pos[jx++] = cc; + blanks = FALSE; + } + } /* end for */ + for( ; jx < MAC_MAX_ARGS; jx++ ) + { + mac_arg_pos[jx] = 0; + } + for( jx = 0; jx < LINELEN; jx++ ) + { + mac_line[jx] = line[jx]; + } + mac_cc = cc; /* Save line and position in line. */ + mac_ptr = mac_bodies[val]; + if( mac_ptr ) scanning_line = FALSE; + else nextLexeme(); + } /* end if macro */ + else if( M_PSEUDO( sym->type )) + { + nextLexeme(); /* Skip symbol */ + scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); + } + else + { + /* Identifier is not a pseudo-op, interpret as load value */ + punchOutObject( clc, getExprs() & 07777 ); + incrementClc(); + } + } + else + { + /* Identifier is a value, interpret as load value */ + punchOutObject( clc, getExprs() & 07777 ); + incrementClc(); + } + break; + } /* end switch */ + break; + } /* end switch */ + } /* end if */ + } /* end while( scanning_line ) */ + } /* end while( TRUE ) */ +} /* onePass() */ + + +/******************************************************************************/ +/* */ +/* Function: getExprs */ +/* */ +/* Synopsis: Or together a list of blank separated expressions, from the */ +/* current lexeme onward. Leave the current lexeme as */ +/* the last one in the list. */ +/* */ +/******************************************************************************/ +WORD32 getExprs() +{ + SYM_T *symv; + SYM_T *symt; + WORD32 temp; + SYMTYP temp_type; + WORD32 value; + SYMTYP value_type; + + symv = getExpr(); + value = symv->val; + value_type = symv->type; + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( value ); + } + switch( line[lexstart] ) + { + case ')': + case ']': + return( value ); + + default: + break; + } + + /* Interpret space as logical or */ + symt = getExpr(); + temp = symt->val & 07777; + temp_type = symt->type; + + switch( value_type ) + { + case MRI: + case MRIFIX: + /* Previous symbol was a Memory Reference Instruction. */ + switch( temp_type ) + { + case MRI: + case MRIFIX: + /* Current symbol is also a Memory Reference Instruction. */ + value |= temp; /* Just OR the MRI instructions. */ + break; + + default: + /* Now have the address part of the MRI instruction. */ + if( temp < 00200 ) + { + value |= temp; /* Page zero MRI. */ + } + else if( (( fieldlc + reloc ) & 07600 ) <= temp + && temp <= (( fieldlc + reloc ) | 0177 )) + { + value |= ( PAGE_BIT | (temp & ADDRESS_FIELD )); /* Current page MRI */ + } + else + { + if(( value & INDIRECT_BIT ) == INDIRECT_BIT ) + { + /* Already indirect, can't generate */ + errorSymbol( &illegal_indirect, symt->name, lexstartprev ); + } + else + { + /* Now fix off page reference. */ + /* Search current page literal pool for needed value. */ + /* Set Indirect Current Page */ + if( testZeroPool( temp )) + { + value |= ( 00400 | insertLiteral( &pz, field, temp )); + } + else + { + value |= ( 00600 | insertLiteral( &cp, clc, temp )); + } + indirect_generated = TRUE; + } + } + break; + } + break; + + default: + value |= temp; /* Normal 12 bit value. */ + break; + } + } /* end while */ +} /* getExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getExpr */ +/* */ +/* Synopsis: Get an expression, from the current lexeme onward, leave the */ +/* current lexeme as the one after the expression. Expressions */ +/* contain terminal symbols (identifiers) separated by operators. */ +/* */ +/******************************************************************************/ +SYM_T *getExpr() +{ + delimiter = line[lexterm]; + + if( line[lexstart] == '-' ) + { + nextLexBlank(); + sym_getexpr = *(eval()); + sym_getexpr.val = ( - sym_getexpr.val ); + } + else + { + sym_getexpr = *(eval()); + } + + + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val += (eval())->val; + break; + + case '-': /* subtract */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val -= (eval())->val; + break; + + case '^': /* multiply */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val *= (eval())->val; + break; + + case '%': /* divide */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val /= (eval())->val; + break; + + case '&': /* and */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val &= (eval())->val; + break; + + case '!': /* or */ + nextLexBlank(); /* skip over the operator */ + sym_getexpr.val |= (eval())->val; + break; + + default: + if( isend( line[lexstart] )) + { + return( &sym_getexpr ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + case ')': + case ']': + case '<': + case ':': + case ',': + break; + + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + sym_getexpr.val = 0; + break; + } + return( &sym_getexpr ); + } + } /* end while */ +} /* getExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: eval */ +/* */ +/* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ +/* */ +/******************************************************************************/ +SYM_T *eval() +{ + WORD32 digit; + WORD32 from; + WORD32 loc; + SYM_T *sym; + WORD32 val; + + val = 0; + + delimiter = line[lexterm]; + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + if( M_UNDEFINED( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &undefined_symbol, sym->name, lexstart ); + } + nextLexeme(); + return( sym ); + } + else if( M_PSEUDO( sym->type )) + { + if( sym->val == DECIMAL ) + { + radix = 10; + } + else if( sym->val == OCTAL ) + { + radix = 8; + } + else if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else if( M_MACRO( sym->type )) + { + if( pass == 2 ) + { + errorSymbol( &misplaced_symbol, sym->name, lexstart ); + } + sym_eval.type = sym->type; + sym_eval.val = 0; + nextLexeme(); + return( &sym_eval ); + } + else + { + nextLexeme(); + return( sym ); + } + } + else if( isdigit( line[lexstart] )) + { + from = lexstart; + val = 0; + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + digit = (WORD32) line[from++] - (WORD32) '0'; + if( digit < radix ) + { + val = val * radix + digit; + } + else + { + errorLexeme( &number_not_radix, from - 1 ); + val = 0; + from = lexterm; + } + } + else + { + errorLexeme( ¬_a_number, lexstart ); + val = 0; + from = lexterm; + } + } + nextLexeme(); + sym_eval.val = val; + return( &sym_eval ); + } + else + { + switch( line[lexstart] ) + { + case '"': /* Character literal */ + if( lexstart + 2 < maxcc ) + { + val = line[lexstart + 1] | 0200; + delimiter = line[lexstart + 2]; + cc = lexstart + 2; + } + else + { + errorMessage( &no_literal_value, lexstart ); + } + nextLexeme(); + break; + + case '.': /* Value of Current Location Counter */ + val = clc + reloc; + nextLexeme(); + break; + + case '[': /* Generate literal on page zero. */ + nextLexBlank(); /* Skip bracket */ + val = getExprs() & 07777; + if( line[lexstart] == ']' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end bracket */ + } + else + { + /* errorMessage( "parens", lexstart ); */ + } + sym_eval.val = insertLiteral( &pz, field, val ); + return( &sym_eval ); + + case '(': /* Generate literal on current page. */ + nextLexBlank(); /* Skip paren */ + val = getExprs() & 07777; + + if( line[lexstart] == ')' ) + { + delimiter = line[lexterm]; + nextLexeme(); /* Skip end paren */ + } + else + { + /* errorMessage( "parens", NULL ); */ + } + if( testZeroPool( val )) + { + sym_eval.val = insertLiteral( &pz, field, val ); + } + else + { + loc = insertLiteral( &cp, clc, val ); + sym_eval.val = loc + (( clc + reloc ) & 077600 ); + } + return( &sym_eval ); + + default: + switch( line[lexstart] ) + { + case '=': + errorMessage( &illegal_equals, lexstart ); + moveToEndOfLine(); + break; + + default: + errorMessage( &illegal_character, lexstart ); + break; + } + val = 0; /* On error, set value to zero. */ + nextLexBlank(); /* Go past illegal character. */ + } + } + sym_eval.val = val; + return( &sym_eval ); +} /* eval() */ + + +/******************************************************************************/ +/* */ +/* Function: inputDubl */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. */ +/* */ +/******************************************************************************/ +void inputDubl() +{ + WORD32 dublvalue; + BOOL scanning_line; + + scanning_line = TRUE; + do + { + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '+': + delimiter = line[lexterm]; + nextLexBlank(); + case '-': + default: + if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) + { + dublvalue = getDublExprs(); + punchOutObject( clc, (WORD32)(( dublvalue >> 12 ) & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)( dublvalue & 07777 )); + incrementClc(); + } + else + { + return; /* Non-numeric input, back to assembly. */ + } + break; + } /* end switch */ + } /* end if */ + + if( error_in_line ) + { + return; /* Error occurred, exit DUBL input mode. */ + } + } /* end while( scanning_line ) */ + readLine(); + nextLexeme(); + scanning_line = TRUE; + } + while( TRUE ); +} /* inputDubl() */ + + +/******************************************************************************/ +/* */ +/* Function: getDublExprs */ +/* */ +/* Synopsis: Get a DUBL expression. */ +/* */ +/******************************************************************************/ +WORD32 getDublExprs() +{ + WORD32 dublvalue; + + dublvalue = getDublExpr(); + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( dublvalue ); + } + else + { + errorMessage( &illegal_expression, lexstart - 1 ); + return( 0 ); + } + } /* end while */ +} /* getDublExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getDublExpr */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +WORD32 getDublExpr() +{ + WORD32 dublvalue; + + delimiter = line[lexterm]; + if( line[lexstart] == '-' ) + { + nextLexBlank(); + dublvalue = evalDubl( 0 ); + nextLexeme(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)dublvalue > 040000000L ) + { + errorMessage( &dubl_overflow, lexstart ); + dublvalue = 0; + } + dublvalue = -dublvalue; + } + else + { + dublvalue = evalDubl( 0 ); + nextLexeme(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)dublvalue > 037777777L ) + { + errorMessage( &dubl_overflow, lexstart ); + dublvalue = 0; + } + } + + if( is_blank( delimiter )) + { + return( dublvalue ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + return( dublvalue ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + case '-': /* subtract */ + case '^': /* multiply */ + case '%': /* divide */ + case '&': /* and */ + case '!': /* or */ + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + break; + + default: + if( isend( line[lexstart] )) + { + return( dublvalue ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + dublvalue = 0; + break; + } + return( dublvalue ); + } + } /* end while */ +} /* getDublExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: evalDubl */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +WORD32 evalDubl( WORD32 initial_value ) +{ + WORD32 digit; + WORD32 from; + WORD32 dublvalue; + WORD32 olddublvalue; + + overflow = FALSE; + delimiter = line[lexterm]; + from = lexstart; + dublvalue = initial_value; + + while( from < lexterm ) + { + if( isdigit( line[from] )) + { + olddublvalue = dublvalue; + digit = (WORD32)( line[from++] - '0' ); + dublvalue = dublvalue * 10 + digit; + if( dublvalue < olddublvalue ) + { + overflow = TRUE; + } + } + else + { + errorLexeme( ¬_a_number, from ); + dublvalue = 0; + from = lexterm; + } + } + return( dublvalue ); +} /* evalDubl() */ + + +/******************************************************************************/ +/* */ +/* Function: inputFltg */ +/* */ +/* Synopsis: Get the value of the current lexeme as a Floating Point const. */ +/* */ +/******************************************************************************/ +void inputFltg() +{ + FLTG_T *fltg; + BOOL scanning_line; + + fltg_input = TRUE; /* Set lexeme scanner for floating point. */ + scanning_line = TRUE; + while( TRUE ) + { + while( scanning_line ) + { + if( isend( line[lexstart] )) + { + scanning_line = FALSE; + } + else + { + switch( line[lexstart] ) + { + case '/': + scanning_line = FALSE; + break; + + case ';': + nextLexeme(); + break; + + case '+': + delimiter = line[lexterm]; + nextLexBlank(); + case '-': + default: + if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) + { + fltg = getFltgExprs(); + punchOutObject( clc, ( fltg->exponent & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)(( fltg->mantissa >> 12 ) & 07777 )); + incrementClc(); + punchOutObject( clc, (WORD32)( fltg->mantissa & 07777 )); + incrementClc(); + } + else + { + fltg_input = FALSE; /* Reset lexeme scanner. */ + return; /* Non-numeric input, back to assembly. */ + } + break; + } /* end switch */ + } /* end if */ + + if( error_in_line ) + { + fltg_input = FALSE; /* Reset lexeme scanner. */ + return; /* Error occurred, exit FLTG input mode. */ + } + } /* end while( scanning_line ) */ + readLine(); + nextLexeme(); + scanning_line = TRUE; + } +} /* inputFltg() */ + + +/******************************************************************************/ +/* */ +/* Function: getFltgExprs */ +/* */ +/* Synopsis: Get a FLTG expression. */ +/* */ +/******************************************************************************/ +FLTG_T *getFltgExprs() +{ + FLTG_T *fltg; + + fltg = getFltgExpr(); + + while( TRUE ) + { + if( isdone( line[lexstart] )) + { + return( fltg ); + } + else + { + errorMessage( &illegal_expression, lexstart - 1 ); + return( 0 ); + } + } /* end while */ +} /* getFltgExprs() */ + + +/******************************************************************************/ +/* */ +/* Function: getFltgExpr */ +/* */ +/* Synopsis: Get the value of the current lexeme as a double word. The */ +/* number is always considered to have a decimal radix. */ +/* */ +/******************************************************************************/ +FLTG_T *getFltgExpr() +{ + FLTG_T *fltg; + + delimiter = line[lexterm]; + fltg = evalFltg(); + /* Test for any value greater than 23 bits in length. */ + if( (unsigned long int)fltg->mantissa> 077777777L ) + { + errorMessage( &fltg_overflow, lexstart ); + } + + if( is_blank( delimiter )) + { + return( fltg ); + } + + /* Here we assume the current lexeme is the operator separating the */ + /* previous operator from the next, if any. */ + while( TRUE ) + { + /* assert line[lexstart] == delimiter */ + if( is_blank( delimiter )) + { + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = 0; + return( fltg ); + } + + switch( line[lexstart] ) + { + case '+': /* add */ + case '-': /* subtract */ + case '^': /* multiply */ + case '%': /* divide */ + case '&': /* and */ + case '!': /* or */ + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = NULL; + break; + + default: + if( isend( line[lexstart] )) + { + return( fltg ); + } + + switch( line[lexstart] ) + { + case '/': + case ';': + break; + + default: + errorMessage( &illegal_expression, lexstart ); + moveToEndOfLine(); + fltg = NULL; + break; + } + return( fltg ); + } + } /* end while */ +} /* getFltgExpr() */ + + +/******************************************************************************/ +/* */ +/* Function: evalFltg */ +/* */ +/* Synopsis: Get the value of the current lexeme as a floating point value. */ +/* Floating point input is alwasy considered decimal. */ +/* The general format of a floating point number is: */ +/* +-ddd.dddE+-dd where each d is a decimal digit. */ +/* */ +/******************************************************************************/ +FLTG_T *evalFltg() +{ + BYTE current_state; + BYTE current_col; + WORD32 exponent; + FLTG_T *fltg; + WORD32 input_value; + BOOL negate; + BOOL negate_exponent; + BYTE next_state; + int right_digits; + + /* This uses a lexical analyzer to parse the floating point format. */ + static BYTE state_table[][10] = + { + /* 0 1 2 3 4 5 6 Oolumn index */ + /* + - d . E sp p State Comment */ + { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ + { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ + { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ + { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ + { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ + { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ + { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ + { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ + { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ + { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ + /* 10 Completion state */ + /* 11 Error state. */ + }; + + delimiter = line[lexterm]; + fltg = &fltg_ac; + fltg->exponent = 0; + fltg->mantissa = 0; + input_value = 0; + negate = FALSE; + negate_exponent = FALSE; + exponent = 0; + right_digits = 0; + current_state = 0; + + while( TRUE ) + { + /* Classify character. This is the column index. */ + switch( line[lexstart] ) + { + case '+': + current_col = 0; + break; + + case '-': + current_col = 1; + break; + + case '.': + current_col = 3; + break; + + case 'E': case 'e': + current_col = 4; + break; + + default: + if( isdigit( line[lexstart] )) + { + current_col = 2; + } + else if( isdone( line[lexstart] )) + { + current_col = 5; + } + else + { + current_col = 6; + } + break; + } + + next_state = state_table[current_state][current_col]; + + switch( next_state ) + { + case 1: /* - */ + negate = TRUE; + case 2: /* + */ + delimiter = line[lexterm]; /* Move past the + or - character. */ + nextLexBlank(); + break; + + case 3: /* Number (+-ddd) */ + input_value = evalDubl( 0 ); /* Integer part of the number. */ + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 4: + delimiter = line[lexterm]; + nextLexBlank(); /* Move past the . character. */ + break; + + case 5: /* . (+-ddd.ddd) */ + /* Fractional part of the number. */ + input_value = evalDubl( input_value ); + right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 6: /* E (+-ddd.dddE) */ + delimiter = line[lexterm]; /* Move past the E. */ + nextLexBlank(); + break; + + case 7: /* - (+-ddd.dddE-) */ + negate_exponent = TRUE; + case 8: /* + (+-ddd.dddE+) */ + delimiter = line[lexterm]; /* Move past the + or - character. */ + nextLexBlank(); + break; + + case 9: /* # (+-ddd.dddE+-dd) */ + exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ + if( negate_exponent ) { exponent = - exponent; } + nextLexeme(); /* Move past previous lexeme. */ + break; + + case 10: /* Floating number parsed, convert */ + /* the number. */ + /* Get the exponent for the number as input. */ + exponent -= right_digits; + + /* Remove trailing zeros and adjust the exponent accordingly. */ + while(( input_value % 10 ) == 0 ) + { + input_value /= 10; + exponent++; + } + + /* Convert the number to floating point. The number is calculated with */ + /* a 27 bit mantissa to improve precision. The extra 3 bits are */ + /* discarded after the result has been calculated. */ + fltg->exponent = 26; + fltg->mantissa = input_value << 3; + normalizeFltg( fltg ); + + + while( exponent != 0 ) + { + if( exponent < 0 ) + { + /* Decimal point is to the left. */ + fltg->mantissa /= 10; + normalizeFltg( fltg ); + exponent++; + } + else if( exponent > 0 ) + { + /* Decimal point is to the right. */ + fltg->mantissa *= 10; + normalizeFltg( fltg ); + exponent--; + } + } + + /* Discard the extra precsion used for calculating the number. */ + fltg->mantissa >>= 3; + fltg->exponent -= 3; + if( negate ) + { + fltg->mantissa = (- fltg->mantissa ) & 077777777L; + } + return( fltg ); + + case 11: /* Error in format. */ + /* Not a properly constructued floating point number. */ + return( fltg ); + default: + break; + } + /* Set state for next pass through the loop. */ + current_state = next_state; + } +} /* evalFltg() */ + + + +/******************************************************************************/ +/* */ +/* Function: normalizeFltg */ +/* */ +/* Synopsis: Normalize a PDP-8 double precision floating point number. */ +/* */ +/******************************************************************************/ +void normalizeFltg( FLTG_T *fltg ) +{ + /* Normalize the floating point number. */ + if( fltg->mantissa != 0 ) + { + if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) + { + while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) + + { + fltg->mantissa <<= 1; + fltg->exponent--; + } + } + else + { + while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) + { + fltg->mantissa >>= 1; + fltg->exponent++; + } + } + } + else + { + fltg->exponent = 0; + } + return; +} + + +/******************************************************************************/ +/* */ +/* Function: incrementClc */ +/* */ +/* Synopsis: Set the next assembly location. Test for collision with */ +/* the literal tables. */ +/* */ +/******************************************************************************/ +WORD32 incrementClc() +{ + testForLiteralCollision( clc ); + + /* Incrementing the location counter is not to change field setting. */ + clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); + fieldlc = clc & 07777; + return( clc ); +} /* incrementClc() */ + + +/******************************************************************************/ +/* */ +/* Function: testForLiteralCollision */ +/* */ +/* Synopsis: Test the given location for collision with the literal tables. */ +/* */ +/******************************************************************************/ +BOOL testForLiteralCollision( WORD32 loc ) +{ + WORD32 pagelc; + WORD32 pageno; + BOOL result = FALSE; + + pageno = GET_PAGE (loc); + pagelc = loc & 00177; + + if( pageno == 0 ) + { + if( pagelc >= lit_loc[pageno] && !pz.error ) + { + errorMessage( &pz_literal_overflow, -1 ); + pz.error = TRUE; + result = TRUE; + } + } + else + { + if( pagelc >= lit_loc[pageno] && !cp.error ) + { + errorMessage( &literal_overflow, -1 ); + cp.error = TRUE; + result = TRUE; + } + } + return( result ); +} /* testForLiteralCollision() */ + + +/******************************************************************************/ +/* */ +/* Function: readLine */ +/* */ +/* Synopsis: Get next line of input. Print previous line if needed. */ +/* */ +/******************************************************************************/ +void readLine() +{ + BOOL ffseen; + WORD32 ix; + WORD32 iy; + char mc; + char inpline[4*LINELEN]; + + listLine(); /* List previous line if needed. */ + indirect_generated = FALSE; /* Mark no indirect address generated. */ + error_in_line = FALSE; /* No error in line. */ + + if( mac_ptr && ( *mac_ptr == '\0' )) /* End of macro? */ + { + mac_ptr = NULL; + for( ix = 0; ix < LINELEN; ix++ ) + { + line[ix] = mac_line[ix]; /* Restore invoking line. */ + } + cc = lexstartprev = mac_cc; /* Restore cc. */ + maxcc = strlen( line ); /* Restore maxcc. */ + listed = TRUE; /* Already listed. */ + return; + } + + cc = 0; /* Initialize column counter. */ + lexstartprev = 0; + if( mac_ptr ) /* Inside macro? */ + { + maxcc = 0; + do + { + mc = *mac_ptr++; /* Next character. */ + if( islower( mc )) /* Encoded argument number? */ + { + ix = mc - 'a'; /* Convert to index. */ + if( iy = mac_arg_pos[ix] ) + { + do /* Copy argument string. */ + { + line[maxcc++] = mac_line[iy++]; + } while(( mac_line[iy] != ',' ) && ( !is_blank( mac_line[iy] )) && + ( !isend( mac_line[iy] ))); + } + } + else /* Ordinary character, just copy. */ + { + line[maxcc++] = mc; + } + } while( !isend( mc )); + line[maxcc] = '\0'; + listed = nomac_exp; + return; + } + + lineno++; /* Count lines read. */ + listed = FALSE; /* Mark as not listed. */ +READ_LINE: + if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) + { + filix_curr++; /* Advance to next file. */ + if( filix_curr < save_argc ) /* More files? */ + { + fclose( infile ); + if(( infile = fopen( save_argv[filix_curr], "r" )) == NULL ) + { + fprintf( stderr, "%s: cannot open \"%s\"\n", save_argv[0], + save_argv[filix_curr] ); + exit( -1 ); + } + goto READ_LINE; + } + else + { + inpline[0] = '$'; + inpline[1] = '\n'; + inpline[2] = '\0'; + } + } + + /* Remove any tabs from the input line by inserting the required number */ + /* of spaces to simulate 8 character tab stops. */ + ffseen = FALSE; + for( ix = 0, iy = 0; inpline[ix] != '\0'; ix++ ) + { + switch( inpline[ix] ) + { + case '\t': + do + { + line[iy] = ' '; + iy++; + } + while(( iy % 8 ) != 0 ); + break; + + case '\f': + if( !ffseen && list_title_set ) topOfForm( list_title, NULL ); + ffseen = TRUE; + break; + + default: + line[iy] = inpline[ix]; + iy++; + break; + } + } + line[iy] = '\0'; + + /* If the line is terminated by CR-LF, remove, the CR. */ + if( line[iy - 2] == '\r' ) + { + iy--; + line[iy - 1] = line[iy - 0]; + line[iy] = '\0'; + } + maxcc = iy; /* Save the current line length. */ +} /* readLine() */ + + +/******************************************************************************/ +/* */ +/* Function: listLine */ +/* */ +/* Synopsis: Output a line to the listing file. */ +/* */ +/******************************************************************************/ +void listLine() +/* generate a line of listing if not already done! */ +{ + if( listfile != NULL && listed == FALSE ) + { + printLine( line, 0, 0, LINE ); + } +} /* listLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printPageBreak */ +/* */ +/* Synopsis: Output a Top of Form and listing header if new page necessary. */ +/* */ +/******************************************************************************/ +void printPageBreak() +{ + if( page_lineno >= LIST_LINES_PER_PAGE ) + /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ + { + if( !list_title_set ) + { + strcpy( list_title, line ); + if( list_title[strlen(list_title) - 1] == '\n' ) + { + list_title[strlen(list_title) - 1] = '\0'; + } + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + list_title_set = TRUE; + } + topOfForm( list_title, NULL ); + + } +} /* printPageBreak() */ + + +/******************************************************************************/ +/* */ +/* Function: printLine */ +/* */ +/* Synopsis: Output a line to the listing file with new page if necessary. */ +/* */ +/******************************************************************************/ +void printLine( char *line, WORD32 loc, WORD32 val, LINESTYLE_T linestyle ) +{ + if( listfile == NULL ) + { + save_error_count = 0; + return; + } + + printPageBreak(); + + list_lineno++; + page_lineno++; + switch( linestyle ) + { + default: + case LINE: + fprintf( listfile, "%5d ", lineno ); + fputs( line, listfile ); + listed = TRUE; + break; + + case LINE_VAL: + if( !listed ) + { + fprintf( listfile, "%5d %4.4o ", lineno, val ); + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %4.4o\n", val ); + } + break; + + case LINE_LOC_VAL: + if( !listed ) + { + if( indirect_generated && lgm_flag ) + { + fprintf( listfile, "%5d %5.5o %4.4o@ ", lineno, loc, val ); + } + else + { + fprintf( listfile, "%5d %5.5o %4.4o ", lineno, loc, val ); + } + fputs( line, listfile ); + listed = TRUE; + } + else + { + fprintf( listfile, " %5.5o %4.4o\n", loc, val ); + } + break; + + case LOC_VAL: + fprintf( listfile, " %5.5o %4.4o\n", loc, val ); + break; + } + printErrorMessages(); +} /* printLine() */ + + +/******************************************************************************/ +/* */ +/* Function: printErrorMessages */ +/* */ +/* Synopsis: Output any error messages from the current list of errors. */ +/* */ +/******************************************************************************/ +void printErrorMessages() +{ + WORD32 ix; + WORD32 iy; + + if( listfile != NULL ) + { + /* If any errors, display them now. */ + for( iy = 0; iy < save_error_count; iy++ ) + { + printPageBreak(); + fprintf( listfile, "%-18.18s", error_list[iy].mesg ); + if( error_list[iy].col >= 0 ) + { + for( ix = 0; ix < error_list[iy].col; ix++ ) + { + if( line[ix] == '\t' ) + { + putc( '\t', listfile ); + } + else + { + putc( ' ', listfile ); + } + } + fputs( "^", listfile ); + list_lineno++; + page_lineno++; + } + fputs( "\n", listfile ); + } + } + save_error_count = 0; +} /* printErrorMessages() */ + + +/******************************************************************************/ +/* */ +/* Function: endOfBinary */ +/* */ +/* Synopsis: Outputs both literal tables at the end of a binary segment. */ +/* */ +/******************************************************************************/ +void endOfBinary() +{ + /* Points to end of page for () operands. */ + punchLiteralPool( &cp, clc - 1 ); + /* Points to end of page zero for [] operands. */ + punchLiteralPool( &pz, field ); + if( error_in_line ) + listLine(); /* List line if not done yet. */ + return; +} /* endOfBinary() */ + + +/******************************************************************************/ +/* */ +/* Function: punchChecksum */ +/* */ +/* Synopsis: Output a checksum if the current mode requires it and an */ +/* object file exists. */ +/* */ +/******************************************************************************/ +void punchChecksum() +{ + /* If the assembler has output any BIN data output the checksum. */ + if( binary_data_output && !rim_mode ) + { + punchLocObject( 0, checksum ); + } + binary_data_output = FALSE; + checksum = 0; +} /* punchChecksum() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLeader */ +/* */ +/* Synopsis: Generate 2 feet of leader on object file, as per DEC */ +/* documentation. Paper tape has 10 punches per inch. */ +/* */ +/******************************************************************************/ +void punchLeader( WORD32 count ) +{ + WORD32 ix; + + /* If value is zero, set to the default of 2 feet of leader. */ + count = ( count == 0 ) ? 240 : count; + + if( objectfile != NULL ) + { + for( ix = 0; ix < count; ix++ ) + { + fputc( 0200, objectfile ); + } + } +} /* punchLeader() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOrigin */ +/* */ +/* Synopsis: Output an origin to the object file. */ +/* */ +/******************************************************************************/ +void punchOrigin( WORD32 loc ) +{ + punchObject((( loc >> 6 ) & 0077 ) | 0100 ); + punchObject( loc & 0077 ); +} /* punchOrigin() */ + + +/******************************************************************************/ +/* */ +/* Function: punchObject */ +/* */ +/* Synopsis: Put one character to object file and include it in checksum. */ +/* */ +/******************************************************************************/ +void punchObject( WORD32 val ) +{ + val &= 0377; + if( objectfile != NULL ) + { + fputc( val, objectfile ); + checksum += val; + } + binary_data_output = TRUE; +} /* punchObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchOutObject */ +/* */ +/* Synopsis: Output the current line and then then punch value to the */ +/* object file. */ +/* */ +/******************************************************************************/ +void punchOutObject( WORD32 loc, WORD32 val ) +{ + printLine( line, ( field | loc ), val, LINE_LOC_VAL ); + punchLocObject( loc, val ); +} /* punchOutObject() */ + +/******************************************************************************/ +/* */ +/* Function: punchLocObject */ +/* */ +/* Synopsis: Output the word (with origin if rim format) to the object file.*/ +/* */ +/******************************************************************************/ +void punchLocObject( WORD32 loc, WORD32 val ) +{ + if( rim_mode ) + { + punchOrigin( loc ); + } + punchObject(( val >> 6 ) & 0077 ); + punchObject( val & 0077 ); +} /* punchLocObject() */ + + +/******************************************************************************/ +/* */ +/* Function: punchLiteralPool */ +/* */ +/* Synopsis: Output the current page data. */ +/* */ +/******************************************************************************/ +void punchLiteralPool( LPOOL_T *p, WORD32 lpool_page ) +{ + WORD32 loc; + WORD32 pageno; + WORD32 tmplc; + + pageno = GET_PAGE (lpool_page); + lpool_page &= 07600; + + if(( lpool_page == 0 ) && ( p != &pz )) + { + return; /* Don't punch page 0 pool if not asked. */ + } + + if( lit_loc[pageno] < lit_base[pageno] ) + { + if( !rim_mode ) + { + /* Put out origin if not in rim mode. */ + punchOrigin( lit_loc[pageno] | lpool_page ); + } + /* Put the literals in the object file. */ + for( loc = lit_loc[pageno]; loc < lit_base[pageno]; loc++ ) + { + tmplc = loc + lpool_page; + printLine( line, (field | tmplc), p->pool[loc], LOC_VAL ); + punchLocObject( tmplc, p->pool[loc] ); + } + p->error = FALSE; + lit_base[pageno] = lit_loc[pageno]; + } +} /* punchLiteralPool() */ + + +/******************************************************************************/ +/* */ +/* Function: insertLiteral */ +/* */ +/* Synopsis: Add a value to the given literal pool if not already in pool. */ +/* Return the location of the value in the pool. */ +/* */ +/******************************************************************************/ +WORD32 insertLiteral( LPOOL_T *pool, WORD32 pool_page, WORD32 value ) +{ + WORD32 ix; + WORD32 pageno; + LPOOL_T *p; + + p = pool; + pageno = GET_PAGE (pool_page); + + /* If page zero is the current page, make sure that literals are inserted */ + /* in the page zero literal table. */ + if(( pool_page & 07600 ) == 0 ) + { + p = &pz; + } + + /* Search the literal pool for any occurence of the needed value. */ + ix = lit_base[pageno] - 1; + while( ix >= lit_loc[pageno] && p->pool[ix] != value ) + { + ix--; + } + + /* Check if value found in literal pool. If not, then insert value. */ + if( ix < lit_loc[pageno] ) + { + lit_loc[pageno]--; + p->pool[lit_loc[pageno]] = value; + ix = lit_loc[pageno]; + } + return( ix ); +} /* insertLiteral() */ + +/******************************************************************************/ +/* */ +/* Function: testZeroPool */ +/* */ +/* Synopsis: Test for literal in page zero pool. */ +/* */ +/******************************************************************************/ +BOOL testZeroPool( WORD32 value ) +{ + WORD32 ix; + WORD32 pageno; + + pageno = GET_PAGE( field ); + for ( ix = lit_loc[pageno]; ix < lit_base[pageno]; ix++ ) + { + if( pz.pool[ix] == value ) return TRUE; + } + return FALSE; +} + + +/******************************************************************************/ +/* */ +/* Function: printSymbolTable */ +/* */ +/* Synopsis: Output the symbol table. */ +/* */ +/******************************************************************************/ +void printSymbolTable() +{ + int col; + int cx; + char *fmt; + int ix; + char mark; + int page; + int row; + int symbol_base; + int symbol_lines; + + symbol_base = number_of_fixed_symbols; + + for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) + { + topOfForm( list_title, s_symtable ); + symbol_lines = LIST_LINES_PER_PAGE - page_lineno; + + for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) + { + list_lineno++; + page_lineno++; + fprintf( listfile, "%5d", list_lineno ); + + for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) + { + /* Get index of symbol for the current line and column */ + cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; + cx += symbol_base; + + /* Make sure that there is a symbol to be printed. */ + if( number_of_fixed_symbols <= cx && cx < symbol_top ) + { + switch( symtab[cx].type & LABEL ) + { + case LABEL: + fmt = " %c%-6.6s %5.5o "; + break; + + default: + fmt = " %c%-6.6s %4.4o "; + break; + } + + switch( symtab[cx].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + mark = '?'; + break; + + case REDEFINED: + mark = '#'; + break; + + default: + mark = ' '; + break; + } + fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); + ix++; + } + } + fprintf( listfile, "\n" ); + } + } +} /* printSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printPermanentSymbolTable */ +/* */ +/* Synopsis: Output the permanent symbol table to a file suitable for */ +/* being input after the EXPUNGE pseudo-op. */ +/* */ +/******************************************************************************/ +void printPermanentSymbolTable() +{ + int ix; + FILE *permfile; + char *s_type; + + if(( permfile = fopen( permpathname, "w" )) == NULL ) + { + exit( 2 ); + } + + fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); + fprintf( permfile, " EXPUNGE\n/\n" ); + /* Print the memory reference instructions first. */ + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_MRI( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + + s_type = " "; + for( ix = 0; ix < symbol_top; ix++ ) + { + if( M_FIXED( symtab[ix].type )) + { + if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) + { + fprintf( permfile, "%-7s %s=%4.4o\n", + s_type, symtab[ix].name, symtab[ix].val ); + } + } + } + fprintf( permfile, "/\n FIXTAB\n" ); + fclose( permfile ); +} /* printPermanentSymbolTable() */ + + +/******************************************************************************/ +/* */ +/* Function: printCrossReference */ +/* */ +/* Synopsis: Output a cross reference (concordance) for the file being */ +/* assembled. */ +/* */ +/******************************************************************************/ +void printCrossReference() +{ + int ix; + int symbol_base; + int xc; + int xc_index; + int xc_refcount; + int xc_cols; + + /* Force top of form for first page. */ + page_lineno = LIST_LINES_PER_PAGE; + + list_lineno = 0; + symbol_base = number_of_fixed_symbols; + + for( ix = symbol_base; ix < symbol_top; ix++ ) + { + list_lineno++; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref ); + } + + fprintf( listfile, "%5d", list_lineno ); + + /* Get reference count & index into concordance table for this symbol. */ + xc_refcount = symtab[ix].xref_count; + xc_index = symtab[ix].xref_index; + /* Determine how to label symbol on concordance. */ + switch( symtab[ix].type & ( DEFINED | REDEFINED )) + { + case UNDEFINED: + fprintf( listfile, " U "); + break; + + case REDEFINED: + fprintf( listfile, " M %5d ", xreftab[xc_index] ); + break; + + default: + fprintf( listfile, " A %5d ", xreftab[xc_index] ); + break; + } + fprintf( listfile, "%-6.6s ", symtab[ix].name ); + + /* Output the references, 8 numbers per line after symbol name. */ + for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) + { + if( xc_cols >= XREF_COLUMNS ) + { + xc_cols = 0; + page_lineno++; + if( page_lineno >= LIST_LINES_PER_PAGE ) + { + topOfForm( list_title, s_xref); + } + list_lineno++; + fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); + } + fprintf( listfile, " %5d", xreftab[xc_index + xc] ); + } + fprintf( listfile, "\n" ); + } +} /* printCrossReference() */ + + +/******************************************************************************/ +/* */ +/* Function: topOfForm */ +/* */ +/* Synopsis: Prints title and sub-title on top of next page of listing. */ +/* */ +/******************************************************************************/ +void topOfForm( char *title, char *sub_title ) +{ + char temp[10]; + + if (!listfile) return; + list_pageno++; + strcpy( temp, s_page ); + sprintf( temp, "%s %d", s_page, list_pageno ); + + /* Output a top of form if not the first page of the listing. */ + if( list_pageno > 1 ) + { + fprintf( listfile, "\f" ); + } + fprintf( listfile, "\n %-63s %10s\n", title, temp ); + + /* Reset the current page line counter. */ + page_lineno = 1; + if( sub_title != NULL ) + { + fprintf( listfile, "%80s\n", sub_title ); + page_lineno++; + } + else + { + fprintf( listfile, "\n" ); + page_lineno++; + } + fprintf( listfile, "\n" ); + page_lineno++; +} /* topOfForm() */ + + +/******************************************************************************/ +/* */ +/* Function: lexemeToName */ +/* */ +/* Synopsis: Convert the current lexeme into a string. */ +/* */ +/******************************************************************************/ +char *lexemeToName( char *name, WORD32 from, WORD32 term ) +{ + WORD32 to; + + to = 0; + + while( from < term && to < ( SYMLEN - 1 )) + { + name[to++] = toupper( line[from++] ); + } + + while( to < SYMLEN ) + { + name[to++] = '\0'; + } + return( name ); +} /* lexemeToName() */ + +/******************************************************************************/ +/* */ +/* Function: defineLexeme */ +/* */ +/* Synopsis: Put lexeme into symbol table with a value. */ +/* */ +/******************************************************************************/ +SYM_T *defineLexeme( WORD32 start, /* start of lexeme being defined. */ + WORD32 term, /* end+1 of lexeme being defined. */ + WORD32 val, /* value of lexeme being defined. */ + SYMTYP type ) /* how symbol is being defined. */ +{ + char name[SYMLEN]; + + lexemeToName( name, start, term); + return( defineSymbol( name, val, type, start )); +} /* defineLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: defineSymbol */ +/* */ +/* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ +/* not already in table. */ +/* */ +/******************************************************************************/ +SYM_T *defineSymbol( char *name, WORD32 val, SYMTYP type, WORD32 start ) +{ + SYM_T *sym; + WORD32 xref_count; + + val = val & 07777; + if( strlen( name ) < 1 ) + { + return( &sym_undefined ); /* Protect against non-existent names. */ + } + sym = lookup( name ); + xref_count = 0; /* Set concordance for normal defintion. */ + + if( M_DEFINED( sym->type ) && sym->val != val && M_NOTRDEF( sym -> type )) + { + if( pass == 2 ) + { + errorSymbol( &redefined_symbol, sym->name, start ); + type = type | REDEFINED; + sym->xref_count++; /* Referenced symbol, count it. */ + xref_count = sym->xref_count; + } + return ( sym ); + } + if( M_FIXED( sym->type )) + { + return ( sym ); + } + + if( pass == 2 && xref ) + { + /* Put the definition line number in the concordance table. */ + /* Defined symbols are not counted as references. */ + xreftab[sym->xref_index] = lineno; + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + xref_count] = lineno; + } + + /* Now set the value and the type. */ + sym->val = val; + sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; + return( sym ); +} /* defineSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: lookup */ +/* */ +/* Synopsis: Find a symbol in table. If not in table, enter symbol in */ +/* table as undefined. Return address of symbol in table. */ +/* */ +/******************************************************************************/ +SYM_T *lookup( char *name ) +{ + int ix; /* Insertion index */ + int lx; /* Left index */ + int rx; /* Right index */ + + /* First search the permanent symbols. */ + lx = 0; + ix = binarySearch( name, lx, number_of_fixed_symbols ); + + /* If symbol not in permanent symbol table. */ + if( ix < 0 ) + { + /* Now try the user symbol table. */ + ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); + + /* If symbol not in user symbol table. */ + if( ix < 0 ) + { + /* Must put symbol in table if index is negative. */ + ix = ~ix; + if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) + { + errorSymbol( &symbol_table_full, name, lexstart ); + exit( 1 ); + } + + for( rx = symbol_top; rx >= ix; rx-- ) + { + symtab[rx + 1] = symtab[rx]; + } + symbol_top++; + + /* Enter the symbol as UNDEFINED with a value of zero. */ + strcpy( symtab[ix].name, name ); + symtab[ix].type = UNDEFINED; + symtab[ix].val = 0; + symtab[ix].xref_count = 0; + if( xref && pass == 2 ) + { + xreftab[symtab[ix].xref_index] = 0; + } + } + } + + return( &symtab[ix] ); /* Return the location of the symbol. */ +} /* lookup() */ + + +/******************************************************************************/ +/* */ +/* Function: binarySearch */ +/* */ +/* Synopsis: Searches the symbol table within the limits given. If the */ +/* symbol is not in the table, it returns the insertion point. */ +/* */ +/******************************************************************************/ +int binarySearch( char *name, int start, int symbol_count ) +{ + int lx; /* Left index */ + int mx; /* Middle index */ + int rx; /* Right index */ + int compare; /* Results of comparison */ + + lx = start; + rx = symbol_count - 1; + while( lx <= rx ) + { + mx = ( lx + rx ) / 2; /* Find center of search area. */ + + compare = strcmp( name, symtab[mx].name ); + + if( compare < 0 ) + { + rx = mx - 1; + } + else if( compare > 0 ) + { + lx = mx + 1; + } + else + { + return( mx ); /* Found a match in symbol table. */ + } + } /* end while */ + return( ~lx ); /* Return insertion point. */ +} /* binarySearch() */ + + +/******************************************************************************/ +/* */ +/* Function: compareSymbols */ +/* */ +/* Synopsis: Used to presort the symbol table when starting assembler. */ +/* */ +/******************************************************************************/ +int compareSymbols( const void *a, const void *b ) +{ + return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); +} /* compareSymbols() */ + +/******************************************************************************/ +/* */ +/* Function: copyMacLine */ +/* */ +/* Synopsis: Used to copy a macro line to the macro buffer. */ +/* */ +/******************************************************************************/ +int copyMacLine( int length, int from, int term, int nargs ) +{ + char name[SYMLEN]; + int ix; + int jx; + int kx; + BOOL bl; + + bl = TRUE; + for( ix = from; ix < term; ix++ ) + { + if( !is_blank( line[ix] )) bl = FALSE; + } + if( bl || ( length < 0 )) return length; + if(( length + term - from + 1) >= MAC_MAX_LENGTH ) return -1; + for( ix = from; ix < term; ) + { + if( nargs && isalpha( line[ix] )) /* Start of symbol? */ + { + for( jx = ix + 1; jx < term; jx++) /* Find end of symbol. */ + { + if( !isalnum( line[jx] )) break; + } + lexemeToName( name, ix, jx ); /* Make into name. */ + for( kx = 0; kx < nargs; kx++ ) /* Compare to arguments. */ + { + if( strncmp( name, &mac_arg_name[kx + 1][0], SYMLEN ) == 0 ) + { + mac_buffer[length++] = 'a' + (char) kx; + for( ix++; ix < jx; ix++ ) + { + mac_buffer[length++] = 'z'; + } + break; + } /* end if strncmp */ + } /* end for kx */ + if( kx >= nargs ) + { + for ( ; ix < jx; ) + { + mac_buffer[length++] = toupper( line[ix++] ); + } + } + } /*end if nargs */ + else + { + mac_buffer[length++] = toupper( line[ix++] ); + } /* end else */ + } /* end for ix */ + mac_buffer[length++] = '\n'; + mac_buffer[length] = 0; + return length; +} + +/******************************************************************************/ +/* */ +/* Function: evalSymbol */ +/* */ +/* Synopsis: Get the pointer for the symbol table entry if exists. */ +/* If symbol doesn't exist, return a pointer to the undefined sym */ +/* */ +/******************************************************************************/ +SYM_T *evalSymbol() +{ + char name[SYMLEN]; + SYM_T *sym; + + sym = lookup( lexemeToName( name, lexstart, lexterm )); + + sym->xref_count++; /* Count the number of references to symbol. */ + + if( xref && pass == 2 ) + { + /* Put the line number in the concordance table. */ + xreftab[sym->xref_index + sym->xref_count] = lineno; + } + + return( sym ); +} /* evalSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: moveToEndOfLine */ +/* */ +/* Synopsis: Move the parser input to the end of the current input line. */ +/* */ +/******************************************************************************/ +void moveToEndOfLine() +{ + while( !isend( line[cc] )) cc++; + lexstart = cc; + lexterm = cc; + lexstartprev = lexstart; +} /* moveToEndOfLine() */ + +/******************************************************************************/ +/* */ +/* Function: nextLexeme */ +/* */ +/* Synopsis: Get the next lexical element from input line. */ +/* */ +/******************************************************************************/ +void nextLexeme() +{ + /* Save start column of previous lexeme for diagnostic messages. */ + lexstartprev = lexstart; + lextermprev = lexterm; + + while( is_blank( line[cc] )) { cc++; } + lexstart = cc; + + if( isalnum( line[cc] )) + { + while( isalnum( line[cc] )) { cc++; } + } + else if( isend( line[cc] )) + { + /* End-of-Line, don't advance cc! */ + } + else + { + switch( line[cc] ) + { + case '"': /* Quoted letter */ + if( cc + 2 < maxcc ) + { + cc++; + cc++; + } + else + { + errorMessage( &no_literal_value, lexstart ); + cc++; + } + break; + + case '/': /* Comment, don't advance cc! */ + break; + + default: /* All other punctuation. */ + cc++; + break; + } + } + lexterm = cc; +} /* nextLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: nextLexBlank */ +/* */ +/* Synopsis: Used to prevent illegal blanks in expressions. */ +/* */ +/******************************************************************************/ +void nextLexBlank() +{ + nextLexeme(); + if( is_blank( delimiter )) + { + errorMessage( &illegal_blank, lexstart - 1 ); + } + delimiter = line[lexterm]; +} /* nextLexBlank() */ + + + +/******************************************************************************/ +/* */ +/* Function: pseudoOperators */ +/* */ +/* Synopsis: Process pseudo-ops (directives). */ +/* */ +/******************************************************************************/ +BOOL pseudoOperators( PSEUDO_T val ) +{ + int count; + int delim; + int index; + int ix; + WORD32 length; + int level; + int lexstartsave; + WORD32 newfield; + WORD32 oldclc; + int pack; + int pageno; + int pos; + int radixprev; + BOOL status; + SYM_T *sym; + WORD32 value; + WORD32 word; + int width; + static int mask_tab[13] = { 00000, + 00001, 00003, 00007, 00017, 00037, 00077, + 00177, 00377, 00777, 01777, 03777, 07777 }; + + status = TRUE; + switch( (PSEUDO_T) val ) + { + case BINPUNCH: + /* If there has been data output and this is a mode switch, set up to */ + /* output data in BIN mode. */ + if( binary_data_output && rim_mode ) + { + for( ix = 0; ix < TOTAL_PAGES; ix++) + { + lit_loc[ix] = lit_base[ix] = 00200; + } + cp.error = FALSE; + pz.error = FALSE; + punchLeader( 8 ); /* Generate a short leader/trailer. */ + checksum = 0; + binary_data_output = FALSE; + } + rim_mode = FALSE; + break; + + case DECIMAL: + radix = 10; + break; + + case DEFINE: + count = 0; + index = 0; + lexstartsave = lexstart; + while(( line[lexstart] != '<' ) && ( !isdone( line[lexstart] )) && + ( count < MAC_MAX_ARGS )) + { + if ( !isalpha( line[lexstart] ) && ( index == 0 )) + { + index = lexstart; + } + lexemeToName( &mac_arg_name[count++][0], lexstart, lexterm ); + nextLexeme(); + } + if( count == 0 ) /* No macro name. */ + { + errorMessage( &no_macro_name, lexstartsave ); + index = 1; + } + else if( index ) /* Bad argument name. */ + { + errorMessage( &bad_dummy_arg, index ); + } + else if( mac_count >= MAC_TABLE_LENGTH ) + { + errorMessage( ¯o_table_full, lexstartsave ); + index = 1; + } + else + { + value = mac_count; + mac_count++; /* Value is entry in mac_bodies. */ + defineSymbol( &mac_arg_name[0][0], value, MACRO, lexstartsave ); + } + if( isend( line[lexstart] ) || ( line[lexstart] == '/' )) + { + readLine(); + nextLexeme(); + } + if( index ) + { + conditionFalse(); /* On error skip macro body. */ + } + else if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + index = lexstart + 1; + length = 0; + level = 1; + while( level > 0 ) + { + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + length = copyMacLine( length, index, cc, count - 1 ); + readLine(); + index = 0; + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + case '$': + level = 0; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + length = copyMacLine( length, index, cc - 1, count - 1 ); + if( length < 0 ) + { + errorMessage (¯o_too_long, lexstart ); + } + else if( length == 0 ) + { + mac_bodies[value] = NULL; + } + else + { + mac_bodies[value] = (char *) malloc( length + 1 ); + if( mac_bodies[value] ) + { + strncpy( mac_bodies[value], mac_buffer, length ); + *( mac_bodies[value] + length ) = 0; + } + else + { + errorMessage( &no_virtual_memory, lexstart ); + } + } + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } /* end if */ + break; + + case DUBL: + inputDubl(); + break; + + case EJECT: + page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ + status = FALSE; /* This will force reading of next line */ + break; + + case ENPUNCH: + if( pass == 2 ) + { + objectfile = objectsave; + } + break; + + case EXPUNGE: /* Erase symbol table */ + if( pass == 1 ) + { + symtab[0] = sym_undefined; + symbol_top = 0; + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Enter the pseudo-ops into the symbol table. */ + for( ix = 0; ix < DIM( pseudo ); ix++ ) + { + defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); + } + /* Enter I and Z into the symbol table. */ + for( ix = 0; ix < 2; ix++ ) + { + defineSymbol( permanent_symbols[ix].name, + permanent_symbols[ix].val, + permanent_symbols[ix].type | DEFFIX , 0 ); + } + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + } + break; + + case BANK: + case FIELD: + punchLiteralPool( &cp, clc - 1 ); + punchLiteralPool( &pz, field ); + newfield = field >> 12; + lexstartsave = lexstartprev; + if( isdone( line[lexstart] )) + { + newfield += 1; /* Blank FIELD directive. */ + } + else + { + newfield = (getExpr())->val; /* FIELD with argument. */ + } + + if( rim_mode ) + { + errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ + } + else if( newfield > 7 || newfield < 0 ) + { + errorMessage( &illegal_field_value, lexstartprev ); + } + else + { + value = (( newfield & 0007 ) << 3 ) | 00300; + punchObject( value ); + checksum -= value; /* Field punches are not added to checksum. */ + field = newfield << 12; + } + + clc = 0200 | field; + fieldlc = clc & 07777; + + if( !rim_mode ) + { + punchOrigin( clc ); + } + break; + + case FIXTAB: + /* Mark all current symbols as permanent symbols. */ + if( pass == 1) + { + for( ix = 0; ix < symbol_top; ix++ ) + { + symtab[ix].type = ( symtab[ix].type | FIXED ) & ~CONDITION; + if((( symtab[ix].val & 00777 ) == 0 ) && + ( symtab[ix].val <= 05000 ) && + M_DEFINED( symtab[ix].type ) && + !M_PSEUDO( symtab[ix].type ) && + !M_LABEL( symtab[ix].type ) && + !M_MACRO( symtab[ix].type )) + { + symtab[ix].type = symtab[ix].type | MRI; + } + } + number_of_fixed_symbols = symbol_top; + fixed_symbols = &symtab[symbol_top - 1]; + + /* Re-sort the symbol table */ + qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); + } + break; + + case FLTG: + inputFltg(); + /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ + break; + + case IFDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNDEF: + if( isalpha( line[lexstart] )) + { + sym = evalSymbol(); + nextLexeme(); + if( M_DEFINED_CONDITIONALLY( sym->type )) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + } + else + { + errorLexeme( &label_syntax, lexstart ); + } + break; + + case IFNZERO: + if( (getExpr())->val == 0 ) + { + conditionFalse(); + } + else + { + conditionTrue(); + } + break; + + case IFZERO: + if( (getExpr())->val == 0 ) + { + conditionTrue(); + } + else + { + conditionFalse(); + } + break; + + case LGM: + lgm_flag = TRUE; + break; + + case LIST: + listfile = listsave; + break; + + case LIT: + if( clc & 07600 ) + { + punchLiteralPool( &cp, clc ); + } + else + { + punchLiteralPool( &pz, field ); + } + if( !rim_mode ) + { + punchOrigin( clc ); + } + break; + + case LITBAS: + if( clc & 07600 ) + { + punchLiteralPool( &cp, clc ); + } + else + { + punchLiteralPool( &pz, field ); + } + if( !rim_mode ) + { + punchOrigin( clc ); + } + pageno = GET_PAGE (clc); + if( isdone( line[lexstart] )) + { + lit_loc[pageno] = lit_base[pageno] = 0200; + } + else + { + lit_loc[pageno] = lit_base[pageno] = (((getExpr())->val) & 0177) + 1; + } + break; + + case NOLGM: + lgm_flag = FALSE; + break; + + case NOPUNCH: + if( pass == 2 ) + { + objectfile = NULL; + } + break; + + case OCTAL: + radix = 8; + break; + + case PAGE: + punchLiteralPool( &cp, clc - 1 ); + oldclc = clc; + if( isdone( line[lexstart] )) + { + clc = ( clc + 0177 ) & 077600; /* No argument. */ + fieldlc = clc & 07777; + } + else + { + value = (getExpr())->val; + clc = field + (( value & 037 ) << 7 ); + fieldlc = clc & 07777; + } + testForLiteralCollision( clc ); + + if( !rim_mode && clc != oldclc ) + { + punchOrigin( clc ); + } + break; + + case PAUSE: + break; + + case RELOC: + if( isdone( line[lexstart] )) + { + reloc = 0; /* Blank RELOC directive. */ + } + else + { + value = (getExpr())->val; /* RELOC with argument. */ + reloc = value - ( clc + reloc ); + } + break; + + case RIMPUNCH: + /* If the assembler has output any BIN data, output the literal tables */ + /* and the checksum for what has been assembled and setup for RIM mode. */ + if( binary_data_output && !rim_mode ) + { + endOfBinary(); + punchChecksum(); + punchLeader( 8 ); /* Generate a short leader/trailer. */ + } + rim_mode = TRUE; + break; + + case TEXT: + delim = line[lexstart]; + pack = 0; + count = 0; + index = lexstart + 1; + while( line[index] != delim && !isend( line[index] )) + { + pack = ( pack << 6 ) | ( line[index] & 077 ); + count++; + if( count > 1 ) + { + punchOutObject( clc, pack ); + incrementClc(); + count = 0; + pack = 0; + } + index++; + } + + if( count != 0 ) + { + punchOutObject( clc, pack << 6 ); + incrementClc(); + } + else + { + punchOutObject( clc, 0 ); + incrementClc(); + } + + if( isend( line[index] )) + { + cc = index; + lexterm = cc; + errorMessage( &text_string, cc ); + } + else + { + cc = index + 1; + lexterm = cc; + } + nextLexeme(); + break; + + case TITLE: + delim = line[lexstart]; + ix = lexstart + 1; + /* Find string delimiter. */ + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( !isend( line[ix] ) ) + { + count = 0; + ix = lexstart + 1; + do + { + if( list_title[ix] == delim && list_title[ix + 1] == delim ) + { + ix++; + } + list_title[count] = line[ix]; + count++; + ix++; + } while( line[ix] != delim && !isend(line[ix]) ); + + if( strlen( list_title ) > TITLELEN ) + { + list_title[TITLELEN] = '\0'; + } + + cc = ix + 1; + lexterm = cc; + page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ + list_title_set = TRUE; + } + else + { + cc = ix; + lexterm = cc; + errorMessage( &text_string, cc ); + } + + nextLexeme(); + break; + + case UNLIST: + listfile = NULL; + break; + + case VFD: + pos = 0; + word = 0; + radixprev = radix; + while( !isdone (line[lexstart] )) + { + lexstartsave = lexstart; + radix = 10; + width = (getExpr())->val; /* Get field width. */ + radix = radixprev; + if( (width <= 0) || ((width + pos) > 12) || (line[lexstart] != ':') ) + { + errorMessage( &illegal_vfd_value, lexstartsave ); + } + nextLexBlank(); /* Skip colon. */ + value = (getExpr())->val; /* Get field value. */ + if( line[lexterm] == ',' ) cc++; + nextLexeme(); /* Advance to next field. */ + pos = pos + width; + if( pos <= 12 ) + { + word = word | ((value & mask_tab[width]) << (12 - pos)); + } + } + punchOutObject( clc, word ); + incrementClc(); + break; + + case ZBLOCK: + value = (getExpr())->val; + if( value < 0 ) + { + errorMessage( &zblock_too_small, lexstartprev ); + } + else if( value + ( clc & 07777 ) - 1 > 07777 ) + { + errorMessage( &zblock_too_large, lexstartprev ); + } + else + { + for( ; value > 0; value-- ) + { + punchOutObject( clc, 0 ); + incrementClc(); + } + } + + break; + + default: + break; + } /* end switch for pseudo-ops */ + return( status ); +} /* pseudoOperators() */ + + +/******************************************************************************/ +/* */ +/* Function: conditionFalse */ +/* */ +/* Synopsis: Called when a false conditional has been evaluated. */ +/* Lex should be the opening <; ignore all text until */ +/* the closing >. */ +/* */ +/******************************************************************************/ +void conditionFalse() +{ + int level; + + if( line[lexstart] == '<' ) + { + /* Invariant: line[cc] is the next unexamined character. */ + level = 1; + while( level > 0 ) + { + if( isend( line[cc] ) || ( line[cc] == '/' )) + { + readLine(); + } + else + { + switch( line[cc] ) + { + case '>': + level--; + cc++; + break; + + case '<': + level++; + cc++; + break; + + case '$': + level = 0; + cc++; + break; + + default: + cc++; + break; + } /* end switch */ + } /* end if */ + } /* end while */ + nextLexeme(); + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionFalse() */ + +/******************************************************************************/ +/* */ +/* Function: conditionTrue */ +/* */ +/* Synopsis: Called when a true conditional has been evaluated. */ +/* Lex should be the opening <; skip it and setup for */ +/* normal assembly. */ +/* */ +/******************************************************************************/ +void conditionTrue() +{ + if( line[lexstart] == '<' ) + { + nextLexeme(); /* Skip the opening '<' */ + } + else + { + errorMessage( <_expected, lexstart ); + } +} /* conditionTrue() */ + + +/******************************************************************************/ +/* */ +/* Function: errorLexeme */ +/* */ +/* Synopsis: Display an error message using the current lexical element. */ +/* */ +/******************************************************************************/ +void errorLexeme( EMSG_T *mesg, WORD32 col ) +{ + char name[SYMLEN]; + + errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); +} /* errorLexeme() */ + + +/******************************************************************************/ +/* */ +/* Function: errorSymbol */ +/* */ +/* Synopsis: Display an error message with a given string. */ +/* */ +/******************************************************************************/ +void errorSymbol( EMSG_T *mesg, char *name, WORD32 col ) +{ + char linecol[12]; + char *s; + + if( pass == 2 ) + { + s = ( name == NULL ) ? "" : name ; + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", + filename, linecol, mesg->file, s, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorSymbol() */ + + +/******************************************************************************/ +/* */ +/* Function: errorMessage */ +/* */ +/* Synopsis: Display an error message without a name argument. */ +/* */ +/******************************************************************************/ +void errorMessage( EMSG_T *mesg, WORD32 col ) +{ + char linecol[12]; + + if( pass == 2 ) + { + errors++; + sprintf( linecol, "(%d:%d)", lineno, col + 1 ); + fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", + filename, linecol, mesg->file, clc ); + saveError( mesg->list, col ); + } + error_in_line = TRUE; +} /* errorMessage() */ + +/******************************************************************************/ +/* */ +/* Function: saveError */ +/* */ +/* Synopsis: Save the current error in a list so it may displayed after the */ +/* the current line is printed. */ +/* */ +/******************************************************************************/ +void saveError( char *mesg, WORD32 col ) +{ + if( save_error_count < DIM( error_list )) + { + error_list[save_error_count].mesg = mesg; + error_list[save_error_count].col = col; + save_error_count++; + } + error_in_line = TRUE; + + if( listed ) + { + printErrorMessages(); + } +} /* saveError() */ +/* End-of-File */ diff --git a/extracters/backup/backup.c b/extracters/backup/backup.c index 8bc6649..1684769 100644 --- a/extracters/backup/backup.c +++ b/extracters/backup/backup.c @@ -1,636 +1,636 @@ - -/* Dump contents of Tops-10 BACKUP tapes, which have been read - into a disk file. The "known good" way to do this is to use the - unix utility "dd", and a command line something like this: - - dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block - - the key thing is that this program expects a fixed block size of - 2720 bytes. If the tape actually has some other format, this - program probably won't succeed. You can use the unix utility "tcopy" - to inspect the contents of the tape. - - Here's the tcopy output from a good tape: - - tcopy /dev/rmt0 - file 0: block size 2720: 9917 records - file 0: eof after 9917 records: 26974240 bytes - eot - total length: 26974240 bytes - -*/ - -#include -#include -#include -#include -#include -#include "backup.h" - -#define bool long -#define false 0 -#define true 1 - -#define RAWSIZE (5*(32+512)) - -#define endof(s) (strchr(s, (char) 0)) - -FILE* source; /* Source "tape". */ - -bool eightbit = false; /* Status of -8 (eight-bit) flag. */ -bool copytape = false; /* Status of -c (copytape fmt) flag. */ -bool buildtree = false; /* Status of -d (build trees) flag. */ -bool interchange = false; /* Status of -i (interchange) flag. */ -bool binary = false; /* Status of -b (binary) flag. */ -bool timfmt = false; /* Status of -m (mts format) flag. */ -long verbose = 0; /* Status of -v (verbose) flag. */ - -char** argfiles; /* File spec's to extract. */ -long argcount; /* Number of them. */ - -unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */ - -long headlh[32], headrh[32]; /* Header block from tape. */ -long datalh[512], datarh[512]; /* Data block from tape. */ - -long prevSEQ; /* SEQ number of previous block. */ -long currentfilenumber; - -char deferbyte; /* Defered byte for output. */ -long defercount; /* Count of defered output bytes. */ - -bool extracting; -FILE* destination; - -/* Tape information: */ - -char systemname[100]; -char savesetname[100]; - -/* File information: */ - -long a_bsiz; /* For now. */ -long a_alls; -long a_mode; -long a_leng; - -char filedev[100]; /* Device: */ -char filedir[100]; /* [ufd] */ -char filename[100]; /* file name. */ -char fileext[100]; /* extension. */ - -char filespec[7][100]; /* [0]: device:ufd. */ - /* [1-5]: sfd's, stored directly here. */ - /* [6]: file.ext */ - -char cname[100]; /* Canonical name. */ - -/* unpackheader unpacks the header block from the raw stream. */ - -void unpackheader() { - unsigned char* rawptr; - long i, left, right; - unsigned char c; - - rawptr = &rawdata[0]; - - for (i = 0; i < 32; i++) { - left = *(rawptr++) << 10; - left |= *(rawptr++) << 2; - left |= (c = *(rawptr++)) >> 6; - right = (c & 077) << 12; - right |= *(rawptr++) << 4; - right |= *(rawptr++) & 017; - headlh[i] = left; - headrh[i] = right; - if(verbose>1) {printf("\n%i l=%d, r=%d",i,left,right);} - } -} - -/* unpackdata unpacks the data block from the raw stream. */ - -void unpackdata() { - unsigned char* rawptr; - long i, left, right; - unsigned char c; - - rawptr = &rawdata[32*5]; - - for (i = 0; i < 512; i++) { - left = *(rawptr++) << 10; - left |= *(rawptr++) << 2; - left |= (c = *(rawptr++)) >> 6; - right = (c & 077) << 12; - right |= *(rawptr++) << 4; - right |= *(rawptr++) & 017; - datalh[i] = left; - datarh[i] = right; - } -} - -/* pars_36bits reads 36 bits from a machine word. */ - -void pars_36bits(index, store) -long index; -char *store; -{ - long l, r; - - l = datalh[index]; - r = datarh[index]; - - store[0] = r & 0377; - store[1] = (r >> 8) & 0377; - store[2] = ((r >> 16) & 03) | ((l << 2) & 0374); - store[3] = (l >> 6) & 0377; - store[4] = (l >> 14) & 017; - store[5] = store[6] = store[7] = 0; -} - -/* pars_5chars reads five ASCII chars from a machine word. */ - -void pars_5chars(index, store) -long index; -char* store; -{ - long l, r; - - l = datalh[index]; - r = datarh[index]; - - store[0] = (0177 & (l >> 11)); - store[1] = (0177 & (l >> 4)); - store[2] = (0177 & ((l << 3) | ((r >> 15) & 017))); - store[3] = (0177 & (r >> 8)); - store[4] = (0177 & (r >> 1)); -} - -/* pars_asciz stores asciz text from data */ - -void pars_asciz(index, store) -long index; -char* store; -{ - long words; - - words = datarh[index++]; - while ((words--) > 0) { - pars_5chars(index++, store); - store += 5; - } - *store = (char) 0; -} - -/* pars_o_name parses an o$name block from data. */ - -void pars_o_name(index) -long index; -{ - long lastw; - - lastw = index + datarh[index]; - ++index; - while (index < lastw) { - switch (datalh[index]) { - case 0: index = lastw; break; - case 1: pars_asciz(index, filedev); break; - case 2: pars_asciz(index, filename); break; - case 3: pars_asciz(index, fileext); break; - case 32: pars_asciz(index, filedir); break; - case 33: pars_asciz(index, filespec[1]); break; - case 34: pars_asciz(index, filespec[2]); break; - case 35: pars_asciz(index, filespec[3]); break; - case 36: pars_asciz(index, filespec[4]); break; - case 37: pars_asciz(index, filespec[5]); break; - } - index += datarh[index]; - } -} - -void pars_o_attr(index) -long index; -{ - /* parse off file attribute block */ - ++index; - a_bsiz = datarh[index + A_BSIZ]; /* for now... */ - a_alls = datarh[index + A_ALLS]; /* for now... */ - a_mode = datarh[index + A_MODE]; /* for now... */ - a_leng = datarh[index + A_LENG]; /* for now... */ -} - -void pars_o_dirt(index) -long index; -{ - /* parse off directory attribute block */ -} - -void pars_o_sysn(index) -long index; -{ - pars_asciz(index, systemname); -} - -void pars_o_ssnm(index) -long index; -{ - pars_asciz(index, savesetname); -} - -void zerotapeinfo() { - systemname[0] = (char) 0; - savesetname[0] = (char) 0; -} - -void zerofileinfo() { - - filedev[0] = (char) 0; - filedir[0] = (char) 0; - filename[0] = (char) 0; - fileext[0] = (char) 0; - - filespec[0][0] = (char) 0; - filespec[1][0] = (char) 0; - filespec[2][0] = (char) 0; - filespec[3][0] = (char) 0; - filespec[4][0] = (char) 0; - filespec[5][0] = (char) 0; - filespec[6][0] = (char) 0; - - cname[0] = (char) 0; -} - -/* unpackinfo picks non-data information from data block. */ - -void unpackinfo() { - long index; - - unpackdata(); - - index = 0; - while (index < headrh[G_LND]) { - switch (datalh[index]) { - case 1: pars_o_name(index); break; - case 2: pars_o_attr(index); break; - case 3: pars_o_dirt(index); break; - case 4: pars_o_sysn(index); break; - case 5: pars_o_ssnm(index); break; - } - index += datarh[index]; - } -} - -void printtapeinfo() { - if (verbose) { - if (*savesetname != (char) 0) printf("Saveset name: %s\n", savesetname); - if (*systemname != (char) 0) printf("Written on: %s\n", systemname); - } -} - -void downcase(s) -char* s; -{ - while (*s != (char) 0) { - if (isupper(*s)) *s = tolower(*s); - s++; - } -} - -void buildfilenames() { - long i; - - if (*filedev != (char) 0) - sprintf(filespec[0], "%s:%s", filedev, filedir); - else - sprintf(filespec[0], "%s", filedir); - - sprintf(filespec[6], "%s.%s", filename, fileext); - - for(i = 0; i < 7; i++) - downcase(filespec[i]); - - sprintf(cname, "%s", filespec[0]); - for(i = 1; i < 6; i++) { - if (*filespec[i] != (char) 0) sprintf(endof(cname), ".%s", filespec[i]); - } - if (*cname != (char) 0) - sprintf(endof(cname), "..%s", filespec[6]); - else - sprintf(cname, "%s", filespec[6]); - -} - -void printfileinfo() { - - buildfilenames(); - printf("%3d %s", currentfilenumber, cname); - if (verbose) { - printf(" (%d) alloc:%d, mode:%o, len:%d", a_bsiz, a_alls, a_mode, a_leng); - } - printf("\n"); -} - -/* readblock reads one logical block from the input stream. */ -/* The header is unpacked into head{l,r}; the data is not. */ - -long blockn=0; - -void readblock() { - long i, bytes; - unsigned char bc[4]; - - i = fread(bc, sizeof(char), 4, source); - if (i == 0) return; - bytes = ((long) bc[1] << 8) | (bc[0]); - if (bytes == 0) return; - if (bytes != RAWSIZE) - fprintf(stderr, "backup: incorrect block size = %d\n", bytes); - i = fread(rawdata, sizeof(char), RAWSIZE, source); - blockn++; - while (i++ < RAWSIZE) rawdata[i] = (char) 0; - fread(bc, sizeof(char), 4, source); - unpackheader(); -} - -/* Disk file output routines: */ - -void WriteBlock() { - char buffer[5*512]; - char binbuf[8*512]; - long bufpos, index; - - for (index = 0; index < 5*512; index++) buffer[index] = 0; - for (index = 0; index < 8*512; index++) binbuf[index] = 0; - - unpackdata(); - if (binary) { - for (index = headrh[G_LND], bufpos = 0; - index < (headrh[G_LND] + headrh[G_SIZE]); index++) { - pars_36bits(index, &binbuf[bufpos]); - bufpos += 8; - } - (void) fwrite(binbuf, sizeof(char), bufpos, destination); - } - else { - for (index = headrh[G_LND], bufpos = 0; - index < (headrh[G_LND] + headrh[G_SIZE]); index++) { - pars_5chars(index, &buffer[bufpos]); - bufpos += 5; - } - - if (headlh[G_FLAGS] & GF_EOF) { - for (index = 1; (index < (eightbit ? 4 : 5)) && (bufpos > 0); index++) { - if (buffer[bufpos - 1] == (char) 0) bufpos--; - } - } - (void) fwrite(buffer, sizeof(char), bufpos, destination); - } -} - -/* OpenOutput opens the output file, according to -d and -i flags. */ - -bool OpenOutput() { - - struct stat statbuf; - char oname[100]; - long i; - - defercount = 0; - - if (interchange) { - destination = fopen(filespec[6], (binary? "wb": "w")); - } else if (!buildtree) { - for (i = 0; (i < sizeof (cname)) && cname[i]; i++) - if (cname[i] == ':') cname[i] = '.'; - destination = fopen(cname, (binary? "wb": "w")); - } else { -/* for(i = 0, oname[0] = (char) 0; i < 6; i++) { - if (*filespec[i] == (char) 0) break; - sprintf(endof(oname), "%s", filespec[i]); - if (stat(oname, &statbuf) != 0) { - if (mkdir(oname, 0777) != 0) { - fprintf(stderr, "backup: cannot create %s/\n", oname); - return(false); - } - } - sprintf(endof(oname), "/"); - } - sprintf(endof(oname), "%s", filespec[6]); - destination = fopen(oname, (binary? "wb": "w")); */ - fprintf(stderr, "backup: tree mode not supported\n"); - return(false); - } - - return(destination != NULL); -} - -void CloseOutput() { - /* Close output file after us. */ -} - -/* Argmatch checks if the current file matches the given argument: */ - -bool argmatch(arg) -char* arg; -{ - long target; - char* f; - char* p; - char* s; - - if (*arg == '#') { - (void) sscanf(arg, "#%d", &target); - return(target == currentfilenumber); - } - - if (*arg == '*') return(1); - - for (f = cname; *f != (char) 0; f++) { - for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++); - if (*s == (char) 0) return (true); - } - return (false); -} - -/* doextract performs the job of "backup -x ..." */ - -void doextract() { - long i; - - currentfilenumber = 0; - extracting = false; - while (!feof(source)) { - readblock(); - if (headrh[G_SEQ] == prevSEQ) continue; - - if (headrh[G_TYPE] == T_FILE) { - if (headlh[G_FLAGS] & GF_SOF) { - currentfilenumber++; - zerofileinfo(); - unpackinfo(); - buildfilenames(); - for (i = 0; i < argcount; i++) { - if (argmatch(argfiles[i])) { - if (*argfiles[i] == '#') { - /* Maybe do a pure shift here? */ - argfiles[i] = argfiles[--argcount]; - } - extracting = true; - break; - } - } - if (extracting) { - if (OpenOutput()) { - if (verbose) { - printf("Extracting %s", cname); - fflush(stdout); - } - } else { - fprintf(stderr, "backup: can't open %s for output\n", cname); - extracting = false; - } - } - } - if (extracting) { - WriteBlock(); - if (headlh[G_FLAGS] & GF_EOF) { - (void) fclose(destination); - extracting = false; - if (verbose) printf("\n"); - if (argcount == 0) - break; - } - } - } - prevSEQ = headrh[G_SEQ]; - } -} - -/* dodirectory performs the job of "backup -t ..." */ - -void dodirectory() { - - currentfilenumber = 0; - - while (!feof(source)) { - readblock(); - if (headrh[G_SEQ] == prevSEQ) continue; - - if (headrh[G_TYPE] == T_BEGIN) { - zerotapeinfo(); - unpackinfo(); - printtapeinfo(); - } - if (headrh[G_TYPE] == T_FILE) { - if (headlh[G_FLAGS] & GF_SOF) { - ++currentfilenumber; - zerofileinfo(); - unpackinfo(); - printfileinfo(); - } - } - prevSEQ = headrh[G_SEQ]; - } -} - -/* command decoder and dispatcher */ - -bool checkarg(arg) -char* arg; -{ - long i; - char c; - - if (*arg == '#') { - if (sscanf(arg, "#%d%c", &i, &c) != 1) { - fprintf(stderr, "backup: bad argument: %s\n", arg); - return(true); - } - } - return(false); -} - - -int main(argc, argv) -long argc; -char* argv[]; -{ - long i; - char* s, tapetype[4]; - bool namenext = false; - bool actgiven = false; - char action; - char* inputname = NULL; - - if (--argc > 0) { - for (s = *(++argv); *s != (char) 0; s++) - switch(*s) { - case '-': - break; - case '8': - eightbit = true; break; - case 'b': - binary = true; break; - case 'c': - copytape = true; break; - case 'd': - buildtree = true; break; - case 'f': - namenext = true; break; - case 'i': - interchange = true; break; - case 'm': - timfmt = true; break; - case 't': - case 'x': - action = *s; actgiven = true; break; - case 'v': - verbose++; break; - default: - fprintf(stderr, "backup: bad option %c\n", *s); - return 0; - } - } - if (namenext) { - if (--argc > 0) - inputname = *(++argv); - else { - fprintf(stderr, "backup: input file name missing\n"); - return 0; - } - } - - argfiles = ++argv; /* Keep rest of arguments. */ - argcount = --argc; /* ... and count 'em. */ - - for (i = 0; i < argcount; i++) { - if (checkarg(argfiles[i])) { - fprintf(stderr, "backup: error in argument %d = %s\n", i, argfiles[i]); - return 0; } } - - if (inputname == NULL) { - /* Use environment var. TAPE here? */ - fprintf(stderr, "backup: no input file given\n"); - return 0; - } - - if (strcmp(inputname, "-") != 0) { - if ((source = fopen(inputname, "rb")) == NULL) { - fprintf(stderr, "backup: can't open %s for input\n", inputname); - return 0; - } - fprintf (stderr, "backup: opening %s for input\n", inputname); - if (timfmt) fread (tapetype, sizeof(char), 4, source); - } else { - source = stdin; - } - - switch (action) { - case 't': dodirectory(); break; - case 'x': doextract(); break; - default: - fprintf(stderr, "backup: internal error in program\n"); - return 0; - } - -} - + +/* Dump contents of Tops-10 BACKUP tapes, which have been read + into a disk file. The "known good" way to do this is to use the + unix utility "dd", and a command line something like this: + + dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block + + the key thing is that this program expects a fixed block size of + 2720 bytes. If the tape actually has some other format, this + program probably won't succeed. You can use the unix utility "tcopy" + to inspect the contents of the tape. + + Here's the tcopy output from a good tape: + + tcopy /dev/rmt0 + file 0: block size 2720: 9917 records + file 0: eof after 9917 records: 26974240 bytes + eot + total length: 26974240 bytes + +*/ + +#include +#include +#include +#include +#include +#include "backup.h" + +#define bool long +#define false 0 +#define true 1 + +#define RAWSIZE (5*(32+512)) + +#define endof(s) (strchr(s, (char) 0)) + +FILE* source; /* Source "tape". */ + +bool eightbit = false; /* Status of -8 (eight-bit) flag. */ +bool copytape = false; /* Status of -c (copytape fmt) flag. */ +bool buildtree = false; /* Status of -d (build trees) flag. */ +bool interchange = false; /* Status of -i (interchange) flag. */ +bool binary = false; /* Status of -b (binary) flag. */ +bool timfmt = false; /* Status of -m (mts format) flag. */ +long verbose = 0; /* Status of -v (verbose) flag. */ + +char** argfiles; /* File spec's to extract. */ +long argcount; /* Number of them. */ + +unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */ + +long headlh[32], headrh[32]; /* Header block from tape. */ +long datalh[512], datarh[512]; /* Data block from tape. */ + +long prevSEQ; /* SEQ number of previous block. */ +long currentfilenumber; + +char deferbyte; /* Defered byte for output. */ +long defercount; /* Count of defered output bytes. */ + +bool extracting; +FILE* destination; + +/* Tape information: */ + +char systemname[100]; +char savesetname[100]; + +/* File information: */ + +long a_bsiz; /* For now. */ +long a_alls; +long a_mode; +long a_leng; + +char filedev[100]; /* Device: */ +char filedir[100]; /* [ufd] */ +char filename[100]; /* file name. */ +char fileext[100]; /* extension. */ + +char filespec[7][100]; /* [0]: device:ufd. */ + /* [1-5]: sfd's, stored directly here. */ + /* [6]: file.ext */ + +char cname[100]; /* Canonical name. */ + +/* unpackheader unpacks the header block from the raw stream. */ + +void unpackheader() { + unsigned char* rawptr; + long i, left, right; + unsigned char c; + + rawptr = &rawdata[0]; + + for (i = 0; i < 32; i++) { + left = *(rawptr++) << 10; + left |= *(rawptr++) << 2; + left |= (c = *(rawptr++)) >> 6; + right = (c & 077) << 12; + right |= *(rawptr++) << 4; + right |= *(rawptr++) & 017; + headlh[i] = left; + headrh[i] = right; + if(verbose>1) {printf("\n%i l=%d, r=%d",i,left,right);} + } +} + +/* unpackdata unpacks the data block from the raw stream. */ + +void unpackdata() { + unsigned char* rawptr; + long i, left, right; + unsigned char c; + + rawptr = &rawdata[32*5]; + + for (i = 0; i < 512; i++) { + left = *(rawptr++) << 10; + left |= *(rawptr++) << 2; + left |= (c = *(rawptr++)) >> 6; + right = (c & 077) << 12; + right |= *(rawptr++) << 4; + right |= *(rawptr++) & 017; + datalh[i] = left; + datarh[i] = right; + } +} + +/* pars_36bits reads 36 bits from a machine word. */ + +void pars_36bits(index, store) +long index; +char *store; +{ + long l, r; + + l = datalh[index]; + r = datarh[index]; + + store[0] = r & 0377; + store[1] = (r >> 8) & 0377; + store[2] = ((r >> 16) & 03) | ((l << 2) & 0374); + store[3] = (l >> 6) & 0377; + store[4] = (l >> 14) & 017; + store[5] = store[6] = store[7] = 0; +} + +/* pars_5chars reads five ASCII chars from a machine word. */ + +void pars_5chars(index, store) +long index; +char* store; +{ + long l, r; + + l = datalh[index]; + r = datarh[index]; + + store[0] = (0177 & (l >> 11)); + store[1] = (0177 & (l >> 4)); + store[2] = (0177 & ((l << 3) | ((r >> 15) & 017))); + store[3] = (0177 & (r >> 8)); + store[4] = (0177 & (r >> 1)); +} + +/* pars_asciz stores asciz text from data */ + +void pars_asciz(index, store) +long index; +char* store; +{ + long words; + + words = datarh[index++]; + while ((words--) > 0) { + pars_5chars(index++, store); + store += 5; + } + *store = (char) 0; +} + +/* pars_o_name parses an o$name block from data. */ + +void pars_o_name(index) +long index; +{ + long lastw; + + lastw = index + datarh[index]; + ++index; + while (index < lastw) { + switch (datalh[index]) { + case 0: index = lastw; break; + case 1: pars_asciz(index, filedev); break; + case 2: pars_asciz(index, filename); break; + case 3: pars_asciz(index, fileext); break; + case 32: pars_asciz(index, filedir); break; + case 33: pars_asciz(index, filespec[1]); break; + case 34: pars_asciz(index, filespec[2]); break; + case 35: pars_asciz(index, filespec[3]); break; + case 36: pars_asciz(index, filespec[4]); break; + case 37: pars_asciz(index, filespec[5]); break; + } + index += datarh[index]; + } +} + +void pars_o_attr(index) +long index; +{ + /* parse off file attribute block */ + ++index; + a_bsiz = datarh[index + A_BSIZ]; /* for now... */ + a_alls = datarh[index + A_ALLS]; /* for now... */ + a_mode = datarh[index + A_MODE]; /* for now... */ + a_leng = datarh[index + A_LENG]; /* for now... */ +} + +void pars_o_dirt(index) +long index; +{ + /* parse off directory attribute block */ +} + +void pars_o_sysn(index) +long index; +{ + pars_asciz(index, systemname); +} + +void pars_o_ssnm(index) +long index; +{ + pars_asciz(index, savesetname); +} + +void zerotapeinfo() { + systemname[0] = (char) 0; + savesetname[0] = (char) 0; +} + +void zerofileinfo() { + + filedev[0] = (char) 0; + filedir[0] = (char) 0; + filename[0] = (char) 0; + fileext[0] = (char) 0; + + filespec[0][0] = (char) 0; + filespec[1][0] = (char) 0; + filespec[2][0] = (char) 0; + filespec[3][0] = (char) 0; + filespec[4][0] = (char) 0; + filespec[5][0] = (char) 0; + filespec[6][0] = (char) 0; + + cname[0] = (char) 0; +} + +/* unpackinfo picks non-data information from data block. */ + +void unpackinfo() { + long index; + + unpackdata(); + + index = 0; + while (index < headrh[G_LND]) { + switch (datalh[index]) { + case 1: pars_o_name(index); break; + case 2: pars_o_attr(index); break; + case 3: pars_o_dirt(index); break; + case 4: pars_o_sysn(index); break; + case 5: pars_o_ssnm(index); break; + } + index += datarh[index]; + } +} + +void printtapeinfo() { + if (verbose) { + if (*savesetname != (char) 0) printf("Saveset name: %s\n", savesetname); + if (*systemname != (char) 0) printf("Written on: %s\n", systemname); + } +} + +void downcase(s) +char* s; +{ + while (*s != (char) 0) { + if (isupper(*s)) *s = tolower(*s); + s++; + } +} + +void buildfilenames() { + long i; + + if (*filedev != (char) 0) + sprintf(filespec[0], "%s:%s", filedev, filedir); + else + sprintf(filespec[0], "%s", filedir); + + sprintf(filespec[6], "%s.%s", filename, fileext); + + for(i = 0; i < 7; i++) + downcase(filespec[i]); + + sprintf(cname, "%s", filespec[0]); + for(i = 1; i < 6; i++) { + if (*filespec[i] != (char) 0) sprintf(endof(cname), ".%s", filespec[i]); + } + if (*cname != (char) 0) + sprintf(endof(cname), "..%s", filespec[6]); + else + sprintf(cname, "%s", filespec[6]); + +} + +void printfileinfo() { + + buildfilenames(); + printf("%3d %s", currentfilenumber, cname); + if (verbose) { + printf(" (%d) alloc:%d, mode:%o, len:%d", a_bsiz, a_alls, a_mode, a_leng); + } + printf("\n"); +} + +/* readblock reads one logical block from the input stream. */ +/* The header is unpacked into head{l,r}; the data is not. */ + +long blockn=0; + +void readblock() { + long i, bytes; + unsigned char bc[4]; + + i = fread(bc, sizeof(char), 4, source); + if (i == 0) return; + bytes = ((long) bc[1] << 8) | (bc[0]); + if (bytes == 0) return; + if (bytes != RAWSIZE) + fprintf(stderr, "backup: incorrect block size = %d\n", bytes); + i = fread(rawdata, sizeof(char), RAWSIZE, source); + blockn++; + while (i++ < RAWSIZE) rawdata[i] = (char) 0; + fread(bc, sizeof(char), 4, source); + unpackheader(); +} + +/* Disk file output routines: */ + +void WriteBlock() { + char buffer[5*512]; + char binbuf[8*512]; + long bufpos, index; + + for (index = 0; index < 5*512; index++) buffer[index] = 0; + for (index = 0; index < 8*512; index++) binbuf[index] = 0; + + unpackdata(); + if (binary) { + for (index = headrh[G_LND], bufpos = 0; + index < (headrh[G_LND] + headrh[G_SIZE]); index++) { + pars_36bits(index, &binbuf[bufpos]); + bufpos += 8; + } + (void) fwrite(binbuf, sizeof(char), bufpos, destination); + } + else { + for (index = headrh[G_LND], bufpos = 0; + index < (headrh[G_LND] + headrh[G_SIZE]); index++) { + pars_5chars(index, &buffer[bufpos]); + bufpos += 5; + } + + if (headlh[G_FLAGS] & GF_EOF) { + for (index = 1; (index < (eightbit ? 4 : 5)) && (bufpos > 0); index++) { + if (buffer[bufpos - 1] == (char) 0) bufpos--; + } + } + (void) fwrite(buffer, sizeof(char), bufpos, destination); + } +} + +/* OpenOutput opens the output file, according to -d and -i flags. */ + +bool OpenOutput() { + + struct stat statbuf; + char oname[100]; + long i; + + defercount = 0; + + if (interchange) { + destination = fopen(filespec[6], (binary? "wb": "w")); + } else if (!buildtree) { + for (i = 0; (i < sizeof (cname)) && cname[i]; i++) + if (cname[i] == ':') cname[i] = '.'; + destination = fopen(cname, (binary? "wb": "w")); + } else { +/* for(i = 0, oname[0] = (char) 0; i < 6; i++) { + if (*filespec[i] == (char) 0) break; + sprintf(endof(oname), "%s", filespec[i]); + if (stat(oname, &statbuf) != 0) { + if (mkdir(oname, 0777) != 0) { + fprintf(stderr, "backup: cannot create %s/\n", oname); + return(false); + } + } + sprintf(endof(oname), "/"); + } + sprintf(endof(oname), "%s", filespec[6]); + destination = fopen(oname, (binary? "wb": "w")); */ + fprintf(stderr, "backup: tree mode not supported\n"); + return(false); + } + + return(destination != NULL); +} + +void CloseOutput() { + /* Close output file after us. */ +} + +/* Argmatch checks if the current file matches the given argument: */ + +bool argmatch(arg) +char* arg; +{ + long target; + char* f; + char* p; + char* s; + + if (*arg == '#') { + (void) sscanf(arg, "#%d", &target); + return(target == currentfilenumber); + } + + if (*arg == '*') return(1); + + for (f = cname; *f != (char) 0; f++) { + for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++); + if (*s == (char) 0) return (true); + } + return (false); +} + +/* doextract performs the job of "backup -x ..." */ + +void doextract() { + long i; + + currentfilenumber = 0; + extracting = false; + while (!feof(source)) { + readblock(); + if (headrh[G_SEQ] == prevSEQ) continue; + + if (headrh[G_TYPE] == T_FILE) { + if (headlh[G_FLAGS] & GF_SOF) { + currentfilenumber++; + zerofileinfo(); + unpackinfo(); + buildfilenames(); + for (i = 0; i < argcount; i++) { + if (argmatch(argfiles[i])) { + if (*argfiles[i] == '#') { + /* Maybe do a pure shift here? */ + argfiles[i] = argfiles[--argcount]; + } + extracting = true; + break; + } + } + if (extracting) { + if (OpenOutput()) { + if (verbose) { + printf("Extracting %s", cname); + fflush(stdout); + } + } else { + fprintf(stderr, "backup: can't open %s for output\n", cname); + extracting = false; + } + } + } + if (extracting) { + WriteBlock(); + if (headlh[G_FLAGS] & GF_EOF) { + (void) fclose(destination); + extracting = false; + if (verbose) printf("\n"); + if (argcount == 0) + break; + } + } + } + prevSEQ = headrh[G_SEQ]; + } +} + +/* dodirectory performs the job of "backup -t ..." */ + +void dodirectory() { + + currentfilenumber = 0; + + while (!feof(source)) { + readblock(); + if (headrh[G_SEQ] == prevSEQ) continue; + + if (headrh[G_TYPE] == T_BEGIN) { + zerotapeinfo(); + unpackinfo(); + printtapeinfo(); + } + if (headrh[G_TYPE] == T_FILE) { + if (headlh[G_FLAGS] & GF_SOF) { + ++currentfilenumber; + zerofileinfo(); + unpackinfo(); + printfileinfo(); + } + } + prevSEQ = headrh[G_SEQ]; + } +} + +/* command decoder and dispatcher */ + +bool checkarg(arg) +char* arg; +{ + long i; + char c; + + if (*arg == '#') { + if (sscanf(arg, "#%d%c", &i, &c) != 1) { + fprintf(stderr, "backup: bad argument: %s\n", arg); + return(true); + } + } + return(false); +} + + +int main(argc, argv) +long argc; +char* argv[]; +{ + long i; + char* s, tapetype[4]; + bool namenext = false; + bool actgiven = false; + char action; + char* inputname = NULL; + + if (--argc > 0) { + for (s = *(++argv); *s != (char) 0; s++) + switch(*s) { + case '-': + break; + case '8': + eightbit = true; break; + case 'b': + binary = true; break; + case 'c': + copytape = true; break; + case 'd': + buildtree = true; break; + case 'f': + namenext = true; break; + case 'i': + interchange = true; break; + case 'm': + timfmt = true; break; + case 't': + case 'x': + action = *s; actgiven = true; break; + case 'v': + verbose++; break; + default: + fprintf(stderr, "backup: bad option %c\n", *s); + return 0; + } + } + if (namenext) { + if (--argc > 0) + inputname = *(++argv); + else { + fprintf(stderr, "backup: input file name missing\n"); + return 0; + } + } + + argfiles = ++argv; /* Keep rest of arguments. */ + argcount = --argc; /* ... and count 'em. */ + + for (i = 0; i < argcount; i++) { + if (checkarg(argfiles[i])) { + fprintf(stderr, "backup: error in argument %d = %s\n", i, argfiles[i]); + return 0; } } + + if (inputname == NULL) { + /* Use environment var. TAPE here? */ + fprintf(stderr, "backup: no input file given\n"); + return 0; + } + + if (strcmp(inputname, "-") != 0) { + if ((source = fopen(inputname, "rb")) == NULL) { + fprintf(stderr, "backup: can't open %s for input\n", inputname); + return 0; + } + fprintf (stderr, "backup: opening %s for input\n", inputname); + if (timfmt) fread (tapetype, sizeof(char), 4, source); + } else { + source = stdin; + } + + switch (action) { + case 't': dodirectory(); break; + case 'x': doextract(); break; + default: + fprintf(stderr, "backup: internal error in program\n"); + return 0; + } + +} + diff --git a/extracters/backup/backup.h b/extracters/backup/backup.h index 436782d..907b5dc 100644 --- a/extracters/backup/backup.h +++ b/extracters/backup/backup.h @@ -1,80 +1,80 @@ - -/* Record types: */ - -#define T_LABEL 1 /* Label. */ -#define T_BEGIN 2 /* Start of SaveSet. */ -#define T_END 3 /* End of SaveSet. */ -#define T_FILE 4 /* File data. */ -#define T_UFD 5 /* UFD data. */ -#define T_EOV 6 /* End of volume. */ -#define T_COMM 7 /* Comment. */ -#define T_CONT 8 /* Continuation. */ - -/* Offsets into header block: */ - -#define G_TYPE 0 /* Record type. */ -#define G_SEQ 1 /* Sequence #. */ -#define G_RTNM 2 /* Relative tape #. */ -#define G_FLAGS 3 /* Flags: */ -#define GF_EOF 0400000 /* End of file. */ -#define GF_RPT 0200000 /* Repeat of last record. */ -#define GF_NCH 0100000 /* Ignore checksum. */ -#define GF_SOF 0040000 /* Start of file. */ -#define G_CHECK 4 /* Checksum. */ -#define G_SIZE 5 /* Size of data in this block. */ -#define G_LND 6 /* Length of non-data block. */ - -/* Non-data block types: */ - -#define O_NAME 1 /* File name. */ -#define O_ATTR 2 /* File attributes. */ -#define O_DIRECT 3 /* Directory attributes. */ -#define O_SYSNAME 4 /* System name block. */ -#define O_SAVESET 5 /* SaveSet name block. */ - -/* Offsets in attribute block: */ - -#define A_FHLN 0 /* header length word */ -#define A_FLGS 1 /* flags */ -#define A_WRIT 2 /* creation date/time */ -#define A_ALLS 3 /* allocated size */ -#define A_MODE 4 /* mode */ -#define A_LENG 5 /* length */ -#define A_BSIZ 6 /* byte size */ -#define A_VERS 7 /* version */ -#define A_PROT 8 /* protection */ -#define A_ACCT 9 /* byte pointer account string */ -#define A_NOTE 10 /* byte pointer to anonotation string */ -#define A_CRET 11 /* creation date/time of this generation */ -#define A_REDT 12 /* last read date/time of this generation */ -#define A_MODT 13 /* monitor set last write date/time */ -#define A_ESTS 14 /* estimated size in words */ -#define A_RADR 15 /* requested disk address */ -#define A_FSIZ 16 /* maximum file size in words */ -#define A_MUSR 17 /* byte ptr to id of last modifier */ -#define A_CUSR 18 /* byte ptr to id of creator */ -#define A_BKID 19 /* byte ptr to save set of previous backup */ -#define A_BKDT 20 /* date/time of last backup */ -#define A_NGRT 21 /* number of generations to retain */ -#define A_NRDS 22 /* nbr opens for read this generation */ -#define A_NWRT 23 /* nbr opens for write this generation */ -#define A_USRW 24 /* user word */ -#define A_PCAW 25 /* privileged customer word */ -#define A_FTYP 26 /* file type and flags */ -#define A_FBSZ 27 /* byte sizes */ -#define A_FRSZ 28 /* record and block sizes */ -#define A_FFFB 29 /* application/customer word */ - -/* T_BEGIN, T_END & T_CONT header offsets: */ - -#define S_DATE 12 -#define S_FORMAT 13 -#define S_BVER 14 -#define S_MONTYP 15 -#define S_SVER 16 -#define S_APR 17 -#define S_DEVICE 18 -#define S_MTCHAR 19 -#define S_REELID 20 -#define S_LTYPE 21 - + +/* Record types: */ + +#define T_LABEL 1 /* Label. */ +#define T_BEGIN 2 /* Start of SaveSet. */ +#define T_END 3 /* End of SaveSet. */ +#define T_FILE 4 /* File data. */ +#define T_UFD 5 /* UFD data. */ +#define T_EOV 6 /* End of volume. */ +#define T_COMM 7 /* Comment. */ +#define T_CONT 8 /* Continuation. */ + +/* Offsets into header block: */ + +#define G_TYPE 0 /* Record type. */ +#define G_SEQ 1 /* Sequence #. */ +#define G_RTNM 2 /* Relative tape #. */ +#define G_FLAGS 3 /* Flags: */ +#define GF_EOF 0400000 /* End of file. */ +#define GF_RPT 0200000 /* Repeat of last record. */ +#define GF_NCH 0100000 /* Ignore checksum. */ +#define GF_SOF 0040000 /* Start of file. */ +#define G_CHECK 4 /* Checksum. */ +#define G_SIZE 5 /* Size of data in this block. */ +#define G_LND 6 /* Length of non-data block. */ + +/* Non-data block types: */ + +#define O_NAME 1 /* File name. */ +#define O_ATTR 2 /* File attributes. */ +#define O_DIRECT 3 /* Directory attributes. */ +#define O_SYSNAME 4 /* System name block. */ +#define O_SAVESET 5 /* SaveSet name block. */ + +/* Offsets in attribute block: */ + +#define A_FHLN 0 /* header length word */ +#define A_FLGS 1 /* flags */ +#define A_WRIT 2 /* creation date/time */ +#define A_ALLS 3 /* allocated size */ +#define A_MODE 4 /* mode */ +#define A_LENG 5 /* length */ +#define A_BSIZ 6 /* byte size */ +#define A_VERS 7 /* version */ +#define A_PROT 8 /* protection */ +#define A_ACCT 9 /* byte pointer account string */ +#define A_NOTE 10 /* byte pointer to anonotation string */ +#define A_CRET 11 /* creation date/time of this generation */ +#define A_REDT 12 /* last read date/time of this generation */ +#define A_MODT 13 /* monitor set last write date/time */ +#define A_ESTS 14 /* estimated size in words */ +#define A_RADR 15 /* requested disk address */ +#define A_FSIZ 16 /* maximum file size in words */ +#define A_MUSR 17 /* byte ptr to id of last modifier */ +#define A_CUSR 18 /* byte ptr to id of creator */ +#define A_BKID 19 /* byte ptr to save set of previous backup */ +#define A_BKDT 20 /* date/time of last backup */ +#define A_NGRT 21 /* number of generations to retain */ +#define A_NRDS 22 /* nbr opens for read this generation */ +#define A_NWRT 23 /* nbr opens for write this generation */ +#define A_USRW 24 /* user word */ +#define A_PCAW 25 /* privileged customer word */ +#define A_FTYP 26 /* file type and flags */ +#define A_FBSZ 27 /* byte sizes */ +#define A_FRSZ 28 /* record and block sizes */ +#define A_FFFB 29 /* application/customer word */ + +/* T_BEGIN, T_END & T_CONT header offsets: */ + +#define S_DATE 12 +#define S_FORMAT 13 +#define S_BVER 14 +#define S_MONTYP 15 +#define S_SVER 16 +#define S_APR 17 +#define S_DEVICE 18 +#define S_MTCHAR 19 +#define S_REELID 20 +#define S_LTYPE 21 + diff --git a/extracters/backup/backup.txt b/extracters/backup/backup.txt index 52c2aa8..55a3f0a 100644 --- a/extracters/backup/backup.txt +++ b/extracters/backup/backup.txt @@ -1,91 +1,91 @@ -Note: This program has no attribution for authorship. I modified it - to deal with binary files. - - Bob Supnik - -Description: - - backup is a program that can list the contents of, and extract - files from a TOPS-10 backup tape. The command syntax is some- - what resembling that of tar. The command - - backup -t[v][f tape] - - lists the contents of 'tape'. The command - - backup -x[cdimv8][f tape] file1 file2 ... - - extracts all files that match either of the file arguments given. - - The names used for the created files will be the canonical names - from the tape, unless -d or -i are in effect. The canonical name - is a string of the format: device.p_pn.sfd1.sfd2..file.ext - -Arguments: - - 'tape' is the name of a tape drive, the name of a file or the - single character '-', meaning stdin. If omitted, the environment - variable TAPE will be consulted. - - - If the "tape" argument is actually a file, which was captured - from a tape (say, on a different machine). The "known good" way - to capture the contents of the tape is to use the unix utility - "dd", and a command line something like this: - - dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block - - the key thing is that this program expects a fixed block size of - 2720 bytes. If the tape actually has some other format, this - program probably won't succeed. You can use the unix utility "tcopy" - to inspect the contents of the tape. - - Here's the tcopy output from a good tape: - - tcopy /dev/rmt0 - file 0: block size 2720: 9917 records - file 0: eof after 9917 records: 26974240 bytes - eot - total length: 26974240 bytes - - - File arguments are either any substring of the canonical name - printed with 'backup -t ...', or a hash mark, and the number - of the file on the tape, or "*" to simply extract all the files. - -Options: - - -b Extract files in binary format. The files on tape have one - 36b word stored in five 8b frames, as specified in PDP-10 - "core dump" mode. The extracted files have one 36b word - stored, right justified, on one little-endian 64b word, as - expected by the simh simulators. - - -c The 'tape' is a disk file, in copytape format. - - -d Create a directory structure when restoring, giving files named - device:p_pn/sfd1/sfd2/file.ext, instead of the flat name space - resulting from using the canonical names. - - -f The disk image file is the next argument. - - -i Ignore device and directory info when building output file names. - - -m The disk image file has a 4 byte header specifying density, which - must be ignored. - - -v Verbose. Does the obvious. -vv does even more. - - -8 Tops-10 file was in eight-bit mode. - - -Bugs: - - We don't handle multiple tape savesets any good. - - We don't check for bad format of input. - - We don't handle checksums, or repeated blocks any good. - - - +Note: This program has no attribution for authorship. I modified it + to deal with binary files. + + Bob Supnik + +Description: + + backup is a program that can list the contents of, and extract + files from a TOPS-10 backup tape. The command syntax is some- + what resembling that of tar. The command + + backup -t[v][f tape] + + lists the contents of 'tape'. The command + + backup -x[cdimv8][f tape] file1 file2 ... + + extracts all files that match either of the file arguments given. + + The names used for the created files will be the canonical names + from the tape, unless -d or -i are in effect. The canonical name + is a string of the format: device.p_pn.sfd1.sfd2..file.ext + +Arguments: + + 'tape' is the name of a tape drive, the name of a file or the + single character '-', meaning stdin. If omitted, the environment + variable TAPE will be consulted. + + + If the "tape" argument is actually a file, which was captured + from a tape (say, on a different machine). The "known good" way + to capture the contents of the tape is to use the unix utility + "dd", and a command line something like this: + + dd if=/dev/rmt0 of=data ibs=2720 obs=2720 conv=block + + the key thing is that this program expects a fixed block size of + 2720 bytes. If the tape actually has some other format, this + program probably won't succeed. You can use the unix utility "tcopy" + to inspect the contents of the tape. + + Here's the tcopy output from a good tape: + + tcopy /dev/rmt0 + file 0: block size 2720: 9917 records + file 0: eof after 9917 records: 26974240 bytes + eot + total length: 26974240 bytes + + + File arguments are either any substring of the canonical name + printed with 'backup -t ...', or a hash mark, and the number + of the file on the tape, or "*" to simply extract all the files. + +Options: + + -b Extract files in binary format. The files on tape have one + 36b word stored in five 8b frames, as specified in PDP-10 + "core dump" mode. The extracted files have one 36b word + stored, right justified, on one little-endian 64b word, as + expected by the simh simulators. + + -c The 'tape' is a disk file, in copytape format. + + -d Create a directory structure when restoring, giving files named + device:p_pn/sfd1/sfd2/file.ext, instead of the flat name space + resulting from using the canonical names. + + -f The disk image file is the next argument. + + -i Ignore device and directory info when building output file names. + + -m The disk image file has a 4 byte header specifying density, which + must be ignored. + + -v Verbose. Does the obvious. -vv does even more. + + -8 Tops-10 file was in eight-bit mode. + + +Bugs: + + We don't handle multiple tape savesets any good. + + We don't check for bad format of input. + + We don't handle checksums, or repeated blocks any good. + + + diff --git a/extracters/ckabstape/ckabstape.c b/extracters/ckabstape/ckabstape.c index 68559d3..b055c5b 100644 --- a/extracters/ckabstape/ckabstape.c +++ b/extracters/ckabstape/ckabstape.c @@ -1,427 +1,427 @@ -#include -#include - -static unsigned char sixlut[64] = { -'@','A','B','C','D','E','F','G', -'H','I','J','K','L','M','N','O', -'P','Q','R','S','T','U','V','W', -'X','Y','Z','[','/',']','^','_', -' ','!','"','#','$','%','&','\'', -'(',')','*','+',',','-','.','/', -'0','1','2','3','4','5','6','7', -'8','9',':',';','<','=','>','?' -}; - -char sixbit(char c) -{ - return(sixlut[c & 0x3f]); -} - -void printb(unsigned char c) -{ - int i; - for(i=0; i<8; i++){ - if((c & 0x80) == 0) - printf("%c",'0'); - else - printf("%c",'1'); - c = c << 1; - } - printf("\n"); -} - -/* - * read an 18 bit paper tape frame - * - * bits 6,7 packed into high part of int so we can look at them - */ -unsigned int readframe() -{ - unsigned int i; - unsigned char c; - do{ - c = getchar(); - if(feof(stdin)) - exit(0); - if((c & 0x80) == 0) printf("{nul}\n"); - } while((c & 0x80) == 0); - - i = ((c & 0xc0)>>6)<<27; - i = i | (c & 0x3f)<<12; - do{ - c = getchar(); - if(feof(stdin)) - exit(0); - if((c & 0x80) == 0) printf("{nul}\n"); - } while((c & 0x80) == 0); - - i = i |((c & 0xc0)>>6)<<24; - i = i | (c & 0x3f)<<6; - - do{ - c = getchar(); - if(feof(stdin)) - exit(0); - if((c & 0x80) == 0) printf("{nul}\n"); - } while((c & 0x80) == 0); - - i = i |((c & 0xc0)>>6)<<21; - i = i | (c & 0x3f); - return i; -} - -void disasm(unsigned int instr) -{ - char *idx; - if(instr & 0010000) idx = ",X"; else idx = " "; - - if((instr & 0700000) != 0700000) - switch(instr & 0740000){ - case(0000000): - printf("CAL%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0040000): - printf("DAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0100000): - printf("JMS%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0140000): - printf("DZM%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0200000): - printf("LAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0240000): - printf("XOR%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0300000): - printf("ADD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0340000): - printf("TAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0400000): - printf("XCT%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0440000): - printf("ISZ%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0500000): - printf("AND%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0540000): - printf("SAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - case(0600000): - printf("JMP%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); - break; - default: - printf("???? "); - break; - } -/* - */ - if((instr & 0700000) == 0700000) - switch(instr){ - case(0700000): - printf("IOT "); - break; - case(0700002): - printf("IOF "); - break; - case(0700004): - printf("CLOF "); - break; - case(0700042): - printf("ION "); - break; - - case(0700101): - printf("RSF (hsrdr)"); - break; - case(0700102): - printf("RCF (hsrdr)"); - break; - case(0700104): - printf("RSA (hsrdr)"); - break; - case(0700112): - printf("RRB (hsrdr)"); - break; - case(0700144): - printf("RSB (hsrdr)"); - break; - - case(0700201): - printf("PSF (hsptp)"); - break; - case(0700202): - printf("PCF (hsptp)"); - break; - case(0700204): - printf("PSA (hsptp)"); - break; - case(0700244): - printf("PSB (hsptp)"); - break; - - case(0700301): - printf("KSF (ttykb)"); - break; - case(0700312): - printf("KRB (ttyrd)"); - break; - case(0700314): - printf("IORS "); - break; - case(0700322): - printf("KRS (ttykb)"); - break; - - case(0700401): - printf("TSF (ttyout)"); - break; - case(0700402): - printf("TCF (ttyout)"); - break; - case(0700406): - printf("TLS (ttyout)"); - break; - - - case(0703302): - printf("CAF "); - break; - - case(0707721): - printf("SBA "); - break; - case(0707722): - case(0707762): - printf("DBA "); - break; - case(0707724): - case(0707764): - printf("EBA "); - break; - -/* - */ - case(0720000): - printf("AAS "); - break; - case(0723000): - printf("AAC "); - break; - case(0725000): - printf("AXS "); - break; - case(0736000): - printf("CLLR "); - break; - case(0735000): - printf("CLX "); - break; - case(0722000): - printf("PAL "); - break; - case(0721000): - printf("PAX "); - break; - case(0730000): - printf("PLA "); - break; - case(0731000): - printf("PLX "); - break; - case(0724000): - printf("PXA "); - break; - case(0726000): - printf("PXL "); - break; - - -/* - * operate instructions - */ - case(0740000): - printf("NOP "); - break; - case(0740001): /* CMA compliment AC */ - printf("CMA "); - break; - case(0740002): - printf("CML "); - break; - case(0740004): - printf("OAS "); - break; - case(0740010): - printf("RAL "); - break; - case(0740020): - printf("RAR "); - break; - case(0740030): - printf("IAC "); - break; - case(0740031): - printf("TCA "); - break; - case(0740040): - printf("HLT "); - break; - case(0740100): - printf("SMA "); - break; - case(0740200): - printf("SZA "); - break; - case(0740400): - printf("SNL "); - break; - case(0741000): - printf("SKP "); - break; - case(0741100): - printf("SPA "); - break; - case(0741200): - printf("SNA "); - break; - case(0741400): - printf("SZL "); - break; - case(0742010): - printf("RTL "); - break; - case(0742020): - printf("RTR "); - break; - case(0742030): - printf("SWHA "); - break; - case(0744000): - printf("CLL "); - break; - case(0744002): - printf("STL "); - break; - case(0744010): - printf("CCL "); - break; - case(0744020): - printf("RCL "); - break; - case(0750000): - printf("CLA "); - break; - case(0750001): - printf("LAS "); - break; - case(0750004): - printf("LAT "); - break; - case(0750010): - printf("GLK "); - break; - case(0760000): - printf("LAW "); - break; - default: - printf("??? "); - break; - } -} - -int main(int argc, char **argv) -{ - int totalblks = 0; - int badblks = 0; - unsigned int currentwd = 0; - int bytecnt = 0; - int col = 0; - int framecount; - unsigned int cksum; - unsigned int adr; - - (void) argc; - (void) argv; - - /* - * read 3 chars and pack them in a word - */ - do { - currentwd = readframe(); - printf("%010o ", currentwd); - printf("%c %c %c ",sixbit((currentwd & 0770000) >>12), - sixbit((currentwd & 007700) >>6), - sixbit(currentwd & 077) ); - col++; - if(col == 4){ - printf("\n"); - col = 0; - } - if(currentwd & 00010000000) { - printf("\n loader end ----\n"); - /* - * start looking for binary data frames - */ - while(1){ - /* - * frames start at the first 0x80 punched - * (check is in readframe..) - */ - - adr = readframe() & 0777777; /*staring adr */ - cksum = adr; - printf("ADR: %010o\n", adr); - - framecount = readframe() & 0777777; /* word count */ - cksum += framecount; - framecount = -(0xfffe0000 | framecount); /* sign extend */ - printf("CNT: %010o (%d)\n", framecount, framecount); - - if((adr & 0700000) != 0){ - printf("FRAMECOUNT == 0 START ADR == %06o\n", adr); - printf("TOTAL BLKS %d TOTAL ERRS %d\n", totalblks, badblks); - exit(0); - } - - currentwd = readframe() & 0777777; /* checksum */ - cksum += currentwd; - printf("CKS: %06o\n", currentwd); - /* - * read all the data words - */ - while(framecount > 0){ - currentwd = readframe(); - cksum += currentwd & 0777777; - printf("%05o: %06o ",adr++, currentwd & 0777777); - disasm(currentwd & 0777777); - printf(" ; %c%c%c ",sixbit((currentwd & 0770000) >>12), - sixbit((currentwd & 007700) >>6), - sixbit(currentwd & 077) ); - printf("\n"); - framecount--; - } - - if(cksum&0777777){ - printf("****BAD CKSUM**** %06o\n",cksum&0777777); - badblks++; - } - - totalblks++; - } - } - } while(!feof(stdin)); - - if(bytecnt > 0){ - printf("%d chrs left\n", bytecnt); - } - - exit(0); -} - +#include +#include + +static unsigned char sixlut[64] = { +'@','A','B','C','D','E','F','G', +'H','I','J','K','L','M','N','O', +'P','Q','R','S','T','U','V','W', +'X','Y','Z','[','/',']','^','_', +' ','!','"','#','$','%','&','\'', +'(',')','*','+',',','-','.','/', +'0','1','2','3','4','5','6','7', +'8','9',':',';','<','=','>','?' +}; + +char sixbit(char c) +{ + return(sixlut[c & 0x3f]); +} + +void printb(unsigned char c) +{ + int i; + for(i=0; i<8; i++){ + if((c & 0x80) == 0) + printf("%c",'0'); + else + printf("%c",'1'); + c = c << 1; + } + printf("\n"); +} + +/* + * read an 18 bit paper tape frame + * + * bits 6,7 packed into high part of int so we can look at them + */ +unsigned int readframe() +{ + unsigned int i; + unsigned char c; + do{ + c = getchar(); + if(feof(stdin)) + exit(0); + if((c & 0x80) == 0) printf("{nul}\n"); + } while((c & 0x80) == 0); + + i = ((c & 0xc0)>>6)<<27; + i = i | (c & 0x3f)<<12; + do{ + c = getchar(); + if(feof(stdin)) + exit(0); + if((c & 0x80) == 0) printf("{nul}\n"); + } while((c & 0x80) == 0); + + i = i |((c & 0xc0)>>6)<<24; + i = i | (c & 0x3f)<<6; + + do{ + c = getchar(); + if(feof(stdin)) + exit(0); + if((c & 0x80) == 0) printf("{nul}\n"); + } while((c & 0x80) == 0); + + i = i |((c & 0xc0)>>6)<<21; + i = i | (c & 0x3f); + return i; +} + +void disasm(unsigned int instr) +{ + char *idx; + if(instr & 0010000) idx = ",X"; else idx = " "; + + if((instr & 0700000) != 0700000) + switch(instr & 0740000){ + case(0000000): + printf("CAL%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0040000): + printf("DAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0100000): + printf("JMS%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0140000): + printf("DZM%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0200000): + printf("LAC%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0240000): + printf("XOR%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0300000): + printf("ADD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0340000): + printf("TAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0400000): + printf("XCT%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0440000): + printf("ISZ%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0500000): + printf("AND%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0540000): + printf("SAD%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + case(0600000): + printf("JMP%c %04o%s ", (instr & 0020000)?'*':' ', instr &07777, idx); + break; + default: + printf("???? "); + break; + } +/* + */ + if((instr & 0700000) == 0700000) + switch(instr){ + case(0700000): + printf("IOT "); + break; + case(0700002): + printf("IOF "); + break; + case(0700004): + printf("CLOF "); + break; + case(0700042): + printf("ION "); + break; + + case(0700101): + printf("RSF (hsrdr)"); + break; + case(0700102): + printf("RCF (hsrdr)"); + break; + case(0700104): + printf("RSA (hsrdr)"); + break; + case(0700112): + printf("RRB (hsrdr)"); + break; + case(0700144): + printf("RSB (hsrdr)"); + break; + + case(0700201): + printf("PSF (hsptp)"); + break; + case(0700202): + printf("PCF (hsptp)"); + break; + case(0700204): + printf("PSA (hsptp)"); + break; + case(0700244): + printf("PSB (hsptp)"); + break; + + case(0700301): + printf("KSF (ttykb)"); + break; + case(0700312): + printf("KRB (ttyrd)"); + break; + case(0700314): + printf("IORS "); + break; + case(0700322): + printf("KRS (ttykb)"); + break; + + case(0700401): + printf("TSF (ttyout)"); + break; + case(0700402): + printf("TCF (ttyout)"); + break; + case(0700406): + printf("TLS (ttyout)"); + break; + + + case(0703302): + printf("CAF "); + break; + + case(0707721): + printf("SBA "); + break; + case(0707722): + case(0707762): + printf("DBA "); + break; + case(0707724): + case(0707764): + printf("EBA "); + break; + +/* + */ + case(0720000): + printf("AAS "); + break; + case(0723000): + printf("AAC "); + break; + case(0725000): + printf("AXS "); + break; + case(0736000): + printf("CLLR "); + break; + case(0735000): + printf("CLX "); + break; + case(0722000): + printf("PAL "); + break; + case(0721000): + printf("PAX "); + break; + case(0730000): + printf("PLA "); + break; + case(0731000): + printf("PLX "); + break; + case(0724000): + printf("PXA "); + break; + case(0726000): + printf("PXL "); + break; + + +/* + * operate instructions + */ + case(0740000): + printf("NOP "); + break; + case(0740001): /* CMA compliment AC */ + printf("CMA "); + break; + case(0740002): + printf("CML "); + break; + case(0740004): + printf("OAS "); + break; + case(0740010): + printf("RAL "); + break; + case(0740020): + printf("RAR "); + break; + case(0740030): + printf("IAC "); + break; + case(0740031): + printf("TCA "); + break; + case(0740040): + printf("HLT "); + break; + case(0740100): + printf("SMA "); + break; + case(0740200): + printf("SZA "); + break; + case(0740400): + printf("SNL "); + break; + case(0741000): + printf("SKP "); + break; + case(0741100): + printf("SPA "); + break; + case(0741200): + printf("SNA "); + break; + case(0741400): + printf("SZL "); + break; + case(0742010): + printf("RTL "); + break; + case(0742020): + printf("RTR "); + break; + case(0742030): + printf("SWHA "); + break; + case(0744000): + printf("CLL "); + break; + case(0744002): + printf("STL "); + break; + case(0744010): + printf("CCL "); + break; + case(0744020): + printf("RCL "); + break; + case(0750000): + printf("CLA "); + break; + case(0750001): + printf("LAS "); + break; + case(0750004): + printf("LAT "); + break; + case(0750010): + printf("GLK "); + break; + case(0760000): + printf("LAW "); + break; + default: + printf("??? "); + break; + } +} + +int main(int argc, char **argv) +{ + int totalblks = 0; + int badblks = 0; + unsigned int currentwd = 0; + int bytecnt = 0; + int col = 0; + int framecount; + unsigned int cksum; + unsigned int adr; + + (void) argc; + (void) argv; + + /* + * read 3 chars and pack them in a word + */ + do { + currentwd = readframe(); + printf("%010o ", currentwd); + printf("%c %c %c ",sixbit((currentwd & 0770000) >>12), + sixbit((currentwd & 007700) >>6), + sixbit(currentwd & 077) ); + col++; + if(col == 4){ + printf("\n"); + col = 0; + } + if(currentwd & 00010000000) { + printf("\n loader end ----\n"); + /* + * start looking for binary data frames + */ + while(1){ + /* + * frames start at the first 0x80 punched + * (check is in readframe..) + */ + + adr = readframe() & 0777777; /*staring adr */ + cksum = adr; + printf("ADR: %010o\n", adr); + + framecount = readframe() & 0777777; /* word count */ + cksum += framecount; + framecount = -(0xfffe0000 | framecount); /* sign extend */ + printf("CNT: %010o (%d)\n", framecount, framecount); + + if((adr & 0700000) != 0){ + printf("FRAMECOUNT == 0 START ADR == %06o\n", adr); + printf("TOTAL BLKS %d TOTAL ERRS %d\n", totalblks, badblks); + exit(0); + } + + currentwd = readframe() & 0777777; /* checksum */ + cksum += currentwd; + printf("CKS: %06o\n", currentwd); + /* + * read all the data words + */ + while(framecount > 0){ + currentwd = readframe(); + cksum += currentwd & 0777777; + printf("%05o: %06o ",adr++, currentwd & 0777777); + disasm(currentwd & 0777777); + printf(" ; %c%c%c ",sixbit((currentwd & 0770000) >>12), + sixbit((currentwd & 007700) >>6), + sixbit(currentwd & 077) ); + printf("\n"); + framecount--; + } + + if(cksum&0777777){ + printf("****BAD CKSUM**** %06o\n",cksum&0777777); + badblks++; + } + + totalblks++; + } + } + } while(!feof(stdin)); + + if(bytecnt > 0){ + printf("%d chrs left\n", bytecnt); + } + + exit(0); +} + diff --git a/extracters/mmdir/mmdir.c b/extracters/mmdir/mmdir.c index d5744ac..30196b4 100644 --- a/extracters/mmdir/mmdir.c +++ b/extracters/mmdir/mmdir.c @@ -1,105 +1,105 @@ -/* This program dumps the directory of a simulated Interdata MDM tape - - Copyright (c) 1993-2002, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. -*/ -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ -int obj, i, k, fc, rc, tpos, sa, ea, fr, fq; -unsigned char b[53]; -unsigned char bca[4]; -unsigned int bc; -int preveof; -FILE *ifile; -#define MAXRLNT 65536 - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: verb file [file...]\n"); - exit (0); } - -for (i = 1; i < argc; i++) { - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - return 0; } - printf ("Processing input file %s\n", argv[i]); - tpos = 0; rc = 1; fc = 0; preveof = 0; - for (;;) { - fseek (ifile, tpos, SEEK_SET); - k = fread (bca, 1, 4, ifile); - bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | - (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); - if ((k == 0) || (bc == 0xFFFFFFFF)) { - printf ("End of physical tape\n"); - break; } - if (bc & 0x80000000) { - printf ("Error marker at record %d\n", rc); - bc = bc & ~0x80000000; } - if (bc == 0) { - if (preveof) { - printf ("End of logical tape\n"); - break; } - preveof = 1; - fc++; obj++; - rc = 1; - tpos = tpos + 4; } - else if (bc > MAXRLNT) { - printf ("Invalid record length %d, terminating\n", bc); - break; } - else { - tpos = tpos + 8 + ((bc + 1) & ~1); - preveof = 0; - if (fc && (rc == 1)) { - if (bc != 52) { - printf ("Invalid record length %d, terminating\n", bc); - break; } - fread (b, 1, 52, ifile); - sa = (((unsigned int) b[18]) << 16) | - (((unsigned int) b[19]) << 8) | ((unsigned int) b[20]); - ea = (((unsigned int) b[21]) << 16) | - (((unsigned int) b[22]) << 8) | ((unsigned int) b[23]); - fr = b[27] >> 4; - fq = b[27] & 0xF; - printf ("%3d %c%c%c 06-%c%c%c", fc, - b[0], b[1], b[2], b[3], b[4], b[5]); - if (fr) printf ("F0%X", fr); - else printf (" "); - printf ("R%c%c%c%c %c%c ", - b[6], b[7], b[25], b[26], b[28], b[29]); - b[18] = b[51] = b[52] = 0; - printf ("%s%s %06X %06X %X\n", - &b[8], &b[30], sa, ea, fq); - } - rc++; } - } - fclose (ifile); - } -return 0; -} +/* This program dumps the directory of a simulated Interdata MDM tape + + Copyright (c) 1993-2002, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ +#include +#include +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ +int obj, i, k, fc, rc, tpos, sa, ea, fr, fq; +unsigned char b[53]; +unsigned char bca[4]; +unsigned int bc; +int preveof; +FILE *ifile; +#define MAXRLNT 65536 + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + return 0; } + printf ("Processing input file %s\n", argv[i]); + tpos = 0; rc = 1; fc = 0; preveof = 0; + for (;;) { + fseek (ifile, tpos, SEEK_SET); + k = fread (bca, 1, 4, ifile); + bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | + (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + if ((k == 0) || (bc == 0xFFFFFFFF)) { + printf ("End of physical tape\n"); + break; } + if (bc & 0x80000000) { + printf ("Error marker at record %d\n", rc); + bc = bc & ~0x80000000; } + if (bc == 0) { + if (preveof) { + printf ("End of logical tape\n"); + break; } + preveof = 1; + fc++; obj++; + rc = 1; + tpos = tpos + 4; } + else if (bc > MAXRLNT) { + printf ("Invalid record length %d, terminating\n", bc); + break; } + else { + tpos = tpos + 8 + ((bc + 1) & ~1); + preveof = 0; + if (fc && (rc == 1)) { + if (bc != 52) { + printf ("Invalid record length %d, terminating\n", bc); + break; } + fread (b, 1, 52, ifile); + sa = (((unsigned int) b[18]) << 16) | + (((unsigned int) b[19]) << 8) | ((unsigned int) b[20]); + ea = (((unsigned int) b[21]) << 16) | + (((unsigned int) b[22]) << 8) | ((unsigned int) b[23]); + fr = b[27] >> 4; + fq = b[27] & 0xF; + printf ("%3d %c%c%c 06-%c%c%c", fc, + b[0], b[1], b[2], b[3], b[4], b[5]); + if (fr) printf ("F0%X", fr); + else printf (" "); + printf ("R%c%c%c%c %c%c ", + b[6], b[7], b[25], b[26], b[28], b[29]); + b[18] = b[51] = b[52] = 0; + printf ("%s%s %06X %06X %X\n", + &b[8], &b[30], sa, ea, fq); + } + rc++; } + } + fclose (ifile); + } +return 0; +} diff --git a/extracters/mtdump/mtdump.c b/extracters/mtdump/mtdump.c index 7941747..aa0833c 100644 --- a/extracters/mtdump/mtdump.c +++ b/extracters/mtdump/mtdump.c @@ -1,148 +1,148 @@ -/* This program dumps the format of a simulated magtape - - Copyright (c) 1993-2007, Robert M. Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not - be used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. - - 07-Mar-07 JDB Fixed precedence error in gap marker test - 30-Aug-06 JDB Added erase gap support -*/ - -#include -#include -#include -#include -#include -#include - -#define F_STD 0 -#define F_E11 1 -#define F_TPC 2 -#define F_TPF 3 - -int main (int argc, char *argv[]) -{ -int obj, i, k, fc, rc, tpos; -unsigned int bc = 0, fmt, rlntsiz; - unsigned char *s; - unsigned char bca[4] = { 0,0,0,0 }; -int preveof, gapcount, gpos = 0, gincr; -FILE *ifile; -#define MAXRLNT 65536 - -if ((argc < 2) || (argv[0] == NULL)) { - printf ("Usage is: mtdump {-secf} file [file...]\n"); - exit (0); } - -s = argv[1]; -if ((s != NULL) && (*s++ == '-')) { - ++argv; --argc; - switch (*s) { - case 'S': case 's': - fmt = F_STD; rlntsiz = 4; break; - case 'E': case 'e': - fmt = F_E11; rlntsiz = 4; break; - case 'C': case 'c': - fmt = F_TPC; rlntsiz = 2; break; - /* case 'F': case 'f': */ -/* fmt = F_TPF; break; */ - default: - fprintf (stderr, "Bad option %c\n", *s); - return 0; - } - } -else { fmt = F_STD; - rlntsiz = 4; } - -for (i = 1; i < argc; i++) { - ifile = fopen (argv[i], "rb"); - if (ifile == NULL) { - printf ("Error opening file: %s\n", argv[i]); - exit (0); } - printf ("Processing input file %s\n", argv[i]); - tpos = 0; rc = 1; fc = 1; preveof = 0; gapcount = 0; - printf ("Processing tape file %d\n", fc); - for (obj = 1;;) { - fseek (ifile, tpos, SEEK_SET); - k = fread (bca, 1, rlntsiz, ifile); - switch (fmt) { - case F_STD: case F_E11: - bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | - (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); - break; - case F_TPC: - bc = (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); - break; - } - - if (k && ((bc == 0xFFFFFFFE) | (bc == 0xFFFEFFFF))) { - if (gapcount == 0) - gpos = tpos; - gincr = (bc == 0xFFFFFFFE ? 4 : 2); - gapcount = gapcount + gincr; - tpos = tpos + gincr; - continue; } - else if (gapcount) { - printf ("Obj %d, position %d, erase gap, length = %d (0x%X)\n", - obj, gpos, gapcount, gapcount); - obj++; - gapcount = 0; } - - if ((k == 0) || (bc == 0xFFFFFFFF)) { - printf ("End of physical tape\n"); - break; } - if (bc & 0x80000000) { - printf ("Error marker at record %d\n", rc); - bc = bc & ~0x80000000; } - if (bc == 0) { - if (preveof) { - printf ("Obj %d, position %d, end of logical tape\n", obj, tpos); - break; } - preveof = 1; - printf ("Obj %d, position %d, end of tape file %d\n", obj, tpos, fc); - fc++; obj++; - rc = 1; - tpos = tpos + rlntsiz; } - else if (bc > MAXRLNT) { - printf ("Invalid record length %d, terminating dump\n", bc); - break; } - else { - if (preveof) printf ("Processing tape file %d\n", fc); - preveof = 0; - printf ("Obj %d, position %d, record %d, length = %d (0x%X)\n", - obj, tpos, rc, bc, bc); - rc++; obj++; - switch (fmt) { - case F_STD: - tpos = tpos + 8 + ((bc + 1) & ~1); break; - case F_E11: - tpos = tpos + 8 + bc; break; - case F_TPC: - tpos = tpos + 2 + ((bc + 1) & ~1); break; - } - } - } - fclose (ifile); - } - -return 0; -} +/* This program dumps the format of a simulated magtape + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 07-Mar-07 JDB Fixed precedence error in gap marker test + 30-Aug-06 JDB Added erase gap support +*/ + +#include +#include +#include +#include +#include +#include + +#define F_STD 0 +#define F_E11 1 +#define F_TPC 2 +#define F_TPF 3 + +int main (int argc, char *argv[]) +{ +int obj, i, k, fc, rc, tpos; +unsigned int bc = 0, fmt, rlntsiz; + unsigned char *s; + unsigned char bca[4] = { 0,0,0,0 }; +int preveof, gapcount, gpos = 0, gincr; +FILE *ifile; +#define MAXRLNT 65536 + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: mtdump {-secf} file [file...]\n"); + exit (0); } + +s = argv[1]; +if ((s != NULL) && (*s++ == '-')) { + ++argv; --argc; + switch (*s) { + case 'S': case 's': + fmt = F_STD; rlntsiz = 4; break; + case 'E': case 'e': + fmt = F_E11; rlntsiz = 4; break; + case 'C': case 'c': + fmt = F_TPC; rlntsiz = 2; break; + /* case 'F': case 'f': */ +/* fmt = F_TPF; break; */ + default: + fprintf (stderr, "Bad option %c\n", *s); + return 0; + } + } +else { fmt = F_STD; + rlntsiz = 4; } + +for (i = 1; i < argc; i++) { + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + printf ("Processing input file %s\n", argv[i]); + tpos = 0; rc = 1; fc = 1; preveof = 0; gapcount = 0; + printf ("Processing tape file %d\n", fc); + for (obj = 1;;) { + fseek (ifile, tpos, SEEK_SET); + k = fread (bca, 1, rlntsiz, ifile); + switch (fmt) { + case F_STD: case F_E11: + bc = (((unsigned int) bca[3]) << 24) | (((unsigned int) bca[2]) << 16) | + (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + break; + case F_TPC: + bc = (((unsigned int) bca[1]) << 8) | ((unsigned int) bca[0]); + break; + } + + if (k && ((bc == 0xFFFFFFFE) | (bc == 0xFFFEFFFF))) { + if (gapcount == 0) + gpos = tpos; + gincr = (bc == 0xFFFFFFFE ? 4 : 2); + gapcount = gapcount + gincr; + tpos = tpos + gincr; + continue; } + else if (gapcount) { + printf ("Obj %d, position %d, erase gap, length = %d (0x%X)\n", + obj, gpos, gapcount, gapcount); + obj++; + gapcount = 0; } + + if ((k == 0) || (bc == 0xFFFFFFFF)) { + printf ("End of physical tape\n"); + break; } + if (bc & 0x80000000) { + printf ("Error marker at record %d\n", rc); + bc = bc & ~0x80000000; } + if (bc == 0) { + if (preveof) { + printf ("Obj %d, position %d, end of logical tape\n", obj, tpos); + break; } + preveof = 1; + printf ("Obj %d, position %d, end of tape file %d\n", obj, tpos, fc); + fc++; obj++; + rc = 1; + tpos = tpos + rlntsiz; } + else if (bc > MAXRLNT) { + printf ("Invalid record length %d, terminating dump\n", bc); + break; } + else { + if (preveof) printf ("Processing tape file %d\n", fc); + preveof = 0; + printf ("Obj %d, position %d, record %d, length = %d (0x%X)\n", + obj, tpos, rc, bc, bc); + rc++; obj++; + switch (fmt) { + case F_STD: + tpos = tpos + 8 + ((bc + 1) & ~1); break; + case F_E11: + tpos = tpos + 8 + bc; break; + case F_TPC: + tpos = tpos + 2 + ((bc + 1) & ~1); break; + } + } + } + fclose (ifile); + } + +return 0; +} diff --git a/extracters/mtdump/mtdump.txt b/extracters/mtdump/mtdump.txt index d85c74f..ffaa8db 100644 --- a/extracters/mtdump/mtdump.txt +++ b/extracters/mtdump/mtdump.txt @@ -1,50 +1,50 @@ -mtdump dumps the record structure of a tape image file in SIMH, E11, TPC, or -P7B format. mtdump is invoked by - - mtdump {-secp} file1 file2 file3... - -where -s specifies SIMH format, -e E11 format, -c TPC format, and -p P7B -format. The default is SIMH format. Output is to stdout, unless redirected. - -SIMH format is as follows: - - 4 byte record length 1 - record 1 - repeat of 4 byte record length 1 - 4 byte record length 2 - record 2 - repeat of 4 byte record length 2 - : - 4 bytes = 00000000 for end of file - -and so on. Records are padded to even byte length. - -E11 format is similar to SIMH format, but odd length records are not padded -to even length. - -TPC format is as follows: - - 2 byte record length 1 - record 1 - 2 byte record length 2 - record 2 - : - 2 bytes = 0000 for end of file - -and so on. Records are padded to even byte length. - -P7B format is confined to 7 track tapes, as follows: - - start of record + first 6b character and parity - : - last character - start of record + first 6b character and parity - -and so on. A P7B tape must have a dummy "last record" with a start of -record indicator. A tape mark is indicated by a character of 317 (start -of record, character 17, wrong parity for odd parity, reserved character -for even parity). - -mmdir is a variant of mtdump that dumps the directory structure of an -Interdata MMD (multimedia diagnostic) tape. The tape must be in SMIH -format. +mtdump dumps the record structure of a tape image file in SIMH, E11, TPC, or +P7B format. mtdump is invoked by + + mtdump {-secp} file1 file2 file3... + +where -s specifies SIMH format, -e E11 format, -c TPC format, and -p P7B +format. The default is SIMH format. Output is to stdout, unless redirected. + +SIMH format is as follows: + + 4 byte record length 1 + record 1 + repeat of 4 byte record length 1 + 4 byte record length 2 + record 2 + repeat of 4 byte record length 2 + : + 4 bytes = 00000000 for end of file + +and so on. Records are padded to even byte length. + +E11 format is similar to SIMH format, but odd length records are not padded +to even length. + +TPC format is as follows: + + 2 byte record length 1 + record 1 + 2 byte record length 2 + record 2 + : + 2 bytes = 0000 for end of file + +and so on. Records are padded to even byte length. + +P7B format is confined to 7 track tapes, as follows: + + start of record + first 6b character and parity + : + last character + start of record + first 6b character and parity + +and so on. A P7B tape must have a dummy "last record" with a start of +record indicator. A tape mark is indicated by a character of 317 (start +of record, character 17, wrong parity for odd parity, reserved character +for even parity). + +mmdir is a variant of mtdump that dumps the directory structure of an +Interdata MMD (multimedia diagnostic) tape. The tape must be in SMIH +format. diff --git a/extracters/ods2/00INSTALL.TXT b/extracters/ods2/00INSTALL.TXT index b04be25..20cc9e3 100644 --- a/extracters/ods2/00INSTALL.TXT +++ b/extracters/ods2/00INSTALL.TXT @@ -1,91 +1,91 @@ -ODS-2 builds on most platforms using the make utility. - -Most of the make logic is in makefile.generic, which is -included by a platform-specific makefile. - -The platform-specific files are: - makefile.unix -- Most Unix/Linux platforms - makefile.solaris -- Solaris - makefile.tru64 -- Tru64/DEC OSF/1 - makefile.nt -- Windows NT - -Select the appropriate platform file & review the definitions. - -For Unix-like OSs, defaults are set in makefile.unixdefs. - -The easiest way to build ODS-2 is to softlink Makefile to -the platform file. For example: - ln -s makefile.unix Makefile - make clean && make - -Alternatively, the following equivalent command can be used: - make -f makefile.unix clean && make -f makefile.unix - -Copy the resulting object file, the .HLB, and .MDF files to a -convenient location on your path, -e.g. /usr/local/bin on unix, or add the build directory to the PATH -environment variable on windows. - -The .HLB files contain the (compiled) help text. The MDF files contain -message strings. Both are binary files, may be architecture-specific, -and can be localized. - -Building from source requires Perl and uses data from VMS message and header files. -Unless you are localizing messages, the included files suffice. The procedure -for extracting localized messages from VMS is documented in the message script.. - -VHD support requires the libvhd in simtools/extracters/libvhd and -simtools/extracters/vhd-tools. Note that these are different from the -vhd support in simH. - -ods2 includes RMS emulation that is fairly complete (except for indexed organization). -Messages use a VMS getmsg emulation; .msg files are localizable. - -If you have an alternate message file (e.g. for I18n), it will be a -.mdf file (e.g. ods2_en_us.mdf), which should also be placed in a convenient -place. - -The help source is in .hlp files, which are structured text. They are compiled -into .hlb files by makehelp. en_us.hlp is the base file; any translations should -use the same naming convention. The makefiles generate ods2_en_us.hlb. The -helpfile can be selected by the ODS2HELP environment variable, and/or the -set helpfile command. - -The default help library filename is "ods2.hlb", in the same directory as -the ods2 executable. This file is made a symlink or copy of the default -language by the makefile. If another available language is desired, -softlink (or copy) the desired language file to it (or set the ODS2HELP - environment variable.) -E.g. for US English help: ln -s ods2_en_us.hlb ods2.hlb - -If you need different definitions for another platform, please -add a new platform-specific file rather than changing makefile.generic. - -For development, I use a wrapper on make called "strictmake" to keep the sources -as clean as possible; it enables extra warnings from gcc (and filters a few). I -also use "grind" to run with valgrind to keep the code leak free. Currently, -the only exception is that libedit doesn't completely clean up on exit. - -On VMS, you can use the DCL command procedure build.com to build -with DECC or GCC, with - $ @build - -build.com will automatically select decc,vaxc or gcc. - -If you have MMS/mmk, use descrip.mms, with - $ mmk -To build with VAXC - $ mmk/macro=vaxc=1 - -For Microsoft Windows, use Visual Studio - the free Community Edition works. -A solution file is included in ODS2's git repository. x64, x86 builds are -supported. Debug builds include Visual Leak Detector (a free tool), but -that can be turned off. VLD indicates that ODS2 is leak-free on Windows. - -I build for Windows with the sources on a Linux share. This makes it easy -to test changes with one copy of the source. The object files do not overlap; -the VS directories are excluded from git. For convenience, the windows .EXEs -are copied to the git working directory. - -Bug reports, contributions, and feature requests are welcome -at https://github.com/open-simh/simtools/issues. +ODS-2 builds on most platforms using the make utility. + +Most of the make logic is in makefile.generic, which is +included by a platform-specific makefile. + +The platform-specific files are: + makefile.unix -- Most Unix/Linux platforms + makefile.solaris -- Solaris + makefile.tru64 -- Tru64/DEC OSF/1 + makefile.nt -- Windows NT + +Select the appropriate platform file & review the definitions. + +For Unix-like OSs, defaults are set in makefile.unixdefs. + +The easiest way to build ODS-2 is to softlink Makefile to +the platform file. For example: + ln -s makefile.unix Makefile + make clean && make + +Alternatively, the following equivalent command can be used: + make -f makefile.unix clean && make -f makefile.unix + +Copy the resulting object file, the .HLB, and .MDF files to a +convenient location on your path, +e.g. /usr/local/bin on unix, or add the build directory to the PATH +environment variable on windows. + +The .HLB files contain the (compiled) help text. The MDF files contain +message strings. Both are binary files, may be architecture-specific, +and can be localized. + +Building from source requires Perl and uses data from VMS message and header files. +Unless you are localizing messages, the included files suffice. The procedure +for extracting localized messages from VMS is documented in the message script.. + +VHD support requires the libvhd in simtools/extracters/libvhd and +simtools/extracters/vhd-tools. Note that these are different from the +vhd support in simH. + +ods2 includes RMS emulation that is fairly complete (except for indexed organization). +Messages use a VMS getmsg emulation; .msg files are localizable. + +If you have an alternate message file (e.g. for I18n), it will be a +.mdf file (e.g. ods2_en_us.mdf), which should also be placed in a convenient +place. + +The help source is in .hlp files, which are structured text. They are compiled +into .hlb files by makehelp. en_us.hlp is the base file; any translations should +use the same naming convention. The makefiles generate ods2_en_us.hlb. The +helpfile can be selected by the ODS2HELP environment variable, and/or the +set helpfile command. + +The default help library filename is "ods2.hlb", in the same directory as +the ods2 executable. This file is made a symlink or copy of the default +language by the makefile. If another available language is desired, +softlink (or copy) the desired language file to it (or set the ODS2HELP + environment variable.) +E.g. for US English help: ln -s ods2_en_us.hlb ods2.hlb + +If you need different definitions for another platform, please +add a new platform-specific file rather than changing makefile.generic. + +For development, I use a wrapper on make called "strictmake" to keep the sources +as clean as possible; it enables extra warnings from gcc (and filters a few). I +also use "grind" to run with valgrind to keep the code leak free. Currently, +the only exception is that libedit doesn't completely clean up on exit. + +On VMS, you can use the DCL command procedure build.com to build +with DECC or GCC, with + $ @build + +build.com will automatically select decc,vaxc or gcc. + +If you have MMS/mmk, use descrip.mms, with + $ mmk +To build with VAXC + $ mmk/macro=vaxc=1 + +For Microsoft Windows, use Visual Studio - the free Community Edition works. +A solution file is included in ODS2's git repository. x64, x86 builds are +supported. Debug builds include Visual Leak Detector (a free tool), but +that can be turned off. VLD indicates that ODS2 is leak-free on Windows. + +I build for Windows with the sources on a Linux share. This makes it easy +to test changes with one copy of the source. The object files do not overlap; +the VS directories are excluded from git. For convenience, the windows .EXEs +are copied to the git working directory. + +Bug reports, contributions, and feature requests are welcome +at https://github.com/open-simh/simtools/issues. diff --git a/extracters/ods2/00README.txt b/extracters/ods2/00README.txt index 229aae3..098842a 100644 --- a/extracters/ods2/00README.txt +++ b/extracters/ods2/00README.txt @@ -1,175 +1,175 @@ -ODS2 provides access to DEC Files-11 On-Disk Structure Level 2 volumes from -Unix, VMS & Windows hosts. - -It is capable of reading and writing physical volumes, such as SCSI disks and -CDROMs, as well as SimH simulator files (native, and optionally VHD). - -The original code was developed by Paul Nankervis, email address: Paulnank@au1.ibm.com. - -This version has been extensively re-worked - debugged, completed and enhanced - by -Timothe Litt - litt@acm.org. It also contains contributions from Larry Baker of -the USGS and from Hunter Goatley. The VHD support is open-source from the Xen -project - with some minor changes on Unix & some significant changes for Windows. - -This version provides full read and write support for ODS2 volumes - physical and -virtual. It can initialize volumes, create, read, write, and extend files. It -allows files to be copied between the host filesystem and the Files-11 volume(s), -converting all RMS formats except indexed. Volume sets and multiple independent volumes -can be accessed simultaneously. - -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 contributions -of the original author and subsequent contributors. This is free software; no warranty -is offered, and while we believe it to be useful, you use it at your own risk. - -There are opportunities for additional contributions, among them: -- message file translations -- help file completion and translations -- testing on other host platforms -Please use the https://github.com/open-simh/simtools repository for pull -requests and issues. - -ODS2 starts by executing a command file, which it looks for in the following order: - The environment variable (or logical name) ODS2INIT - The user's home directory - Windows: %HOMEDRIVE%%HOMEPATH%\.ods2.ini - VMS: SYS$LOGIN:_ODS2.INI - Unix: $HOME/.ods2.ini -This can be used to specify defaults, mount a favorite device... -No error is reported if the file does not exist. - -Next, ODS2 executes any commands specified on the command line. -Multiple command are separated by '$' (most shells require $ to be quoted). - -Finally, ODS2 enters interactive mode. Under Unix, with READLINE support, command -history is preserved in $HOME/.ods2. - -The HELP command provides full command syntax, which is very similar to DCL. -The help text is located in ods2.hlb, in the same directory as ODS2. You can -set the environment variable ODS2HELP to an alternate helpfile or use the -set helpfile command. Note that helpfiles are binary - see 00INSTALL.TXT.. - -N.B. The default help library filename is "ods2.hlb", in the same directory as -the ods2 executable. This file is made a symlink or copy of the default -language by the makefile. If another available language is desired, -softlink (or copy) the desired language file to it (or set the ODS2HELP - environment variable.) -E.g. for US English help: ln -s ods2_en_us.hlb ods2.hlb - -The following is a brief summary of commonly-used command sequences. -Abbreviations can be used. Full VMS filenames (device:[dir.sfd...]name.ext;gen) -can be used for files on the FILES-11 volume. Wildcards are supported for most -commands. Wildcards can be used on the host filesystem, e.g. when copying files -to the volume. - -The most important command for any program: -ODS2$> EXIT - -(QUIT also works.) - -Create a new Files-11 volume: - -ODS2$> INITIALIZE /medium:RM02/LOG rm02.vhd myvolume - -N.B. The .vhd file type will create a dynamic (sparse) VHD-format simulator file. - It is important to specify the medium type, as FILES-11 is sensitve to volume - size and geometry. - -Create a directory: -ODS2$> CREATE/DIRECTORY/LOG [FRED.PAKS] - -Copy files to the volume: -ODS2$> COPY /TO_FILES-11/ASCII *.txt *.doc [FRED.PAKS]*.* - -Set default directory on the volume: -ODS2$> SET DEFAULT [FRED.PAKS] - -See what's there: -ODS2$> DIR /DATE/SIZE - -Copy files from the volume: -ODS2$> COPY /FROM_FILES-11/ASCII *.txt *.* - -Mount an existing volume: -ODS2$> MOUNT /medium:RM02 /WRITE rmo2.vhd myvolume - -N.B. The volume name is optional, but if specified must match. - -Dismount the volume: -ODS2$> DISMOUNT A: -(The drive letter will vary - it's reported by INITIALIZE/MOUNT) - -N.B. It's VERY important to dismount the volume, especially if it's mounted - for write. ODS2 makes heavy use of caching, and any interruption will - leave it in an inconsistent state. - -Inspect a file on the volume: -ODS2$> TYPE/PAGE *.TXT - -Obtain information: -ODS2$> SHOW DEVICES -ODS2$> SHOW VOLUMES - -Interactively create a file: -ODS2$> CREATE FILE.TXT -mumble -gobble -^D -(or ^Z on Windows/VMS) - -Delete a file -ODS2$> DELETE FILE.TXT;2 - -More advanced usage: - -When asked for confirmation, the VMS DCL responses are accepted: - 0, no, false : Don't do this action, ask again for additional items - 1, yes, true : Proceed with this action, ask again for additional items - quit : Don't do this action, and don't consider any more items - all : Proceed with this action, and do all other items without asking - -Lines beginning with ';' or '!' are considered comments, and ignored. This -may be used for commenting command files. - -With editline (libedit) support, a comment entry is made in the command history -when an initialization file is opened. This can be used to trace initialization -issues. editlline also uses ~/.editrc and can be customized using the -'ods2' tag. editline is a Unix command history editor that can be compiled -with ods2. 'man 7 editline' and 'man 5 editrc' for usage details. editline -is available on most Linux distributions; building ods2 requires the corresponding --dev(el) package. - -It may be necessary to quote command arguments, e.g. when specifying a -file name on a Unix system that includes '/'. Use '"' or "'"; double -the quote character to include it in the string. e.g. """hello""" produces - "hello" - -Alternatively, you can switch to unix-style options with SET QUALIFIER_STYLE UNIX. - -A heuristic is used to distinguish physical devices from simulator disk image -files in the mount and initialize commands. Use /device or /image if it -guesses wrong. - -It is possible to mount multiple volumes simultaneously, though not -(currently) to move files directly from one Files-11 volume to another. - -It is possible to mount volumesets, though this hasn't been completely debugged. - -Command files can be executed with @filename. They can be nested. - -Favorite directory and/or copy qualifiers can be set in the .ini file, e.g. -SET DIRECTORY /DATE/SIZE - -With VHD support, you can write to a snapshot of the volume: -ODS2$> MOUNT /SNAPSHOTS_OF=myvolume.vhd testvolume.vhd /write - -For more details on these and other capabilities, use the HELP command. - -For build/installation information, see 00INSTALL.TXT - -Bug reports, contributions, and feature requests are welcome -at https://github.com/open-simh/simtools/issues. - - --- Timothe Litt - April 2016, October 2022 +ODS2 provides access to DEC Files-11 On-Disk Structure Level 2 volumes from +Unix, VMS & Windows hosts. + +It is capable of reading and writing physical volumes, such as SCSI disks and +CDROMs, as well as SimH simulator files (native, and optionally VHD). + +The original code was developed by Paul Nankervis, email address: Paulnank@au1.ibm.com. + +This version has been extensively re-worked - debugged, completed and enhanced - by +Timothe Litt - litt@acm.org. It also contains contributions from Larry Baker of +the USGS and from Hunter Goatley. The VHD support is open-source from the Xen +project - with some minor changes on Unix & some significant changes for Windows. + +This version provides full read and write support for ODS2 volumes - physical and +virtual. It can initialize volumes, create, read, write, and extend files. It +allows files to be copied between the host filesystem and the Files-11 volume(s), +converting all RMS formats except indexed. Volume sets and multiple independent volumes +can be accessed simultaneously. + +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 contributions +of the original author and subsequent contributors. This is free software; no warranty +is offered, and while we believe it to be useful, you use it at your own risk. + +There are opportunities for additional contributions, among them: +- message file translations +- help file completion and translations +- testing on other host platforms +Please use the https://github.com/open-simh/simtools repository for pull +requests and issues. + +ODS2 starts by executing a command file, which it looks for in the following order: + The environment variable (or logical name) ODS2INIT + The user's home directory + Windows: %HOMEDRIVE%%HOMEPATH%\.ods2.ini + VMS: SYS$LOGIN:_ODS2.INI + Unix: $HOME/.ods2.ini +This can be used to specify defaults, mount a favorite device... +No error is reported if the file does not exist. + +Next, ODS2 executes any commands specified on the command line. +Multiple command are separated by '$' (most shells require $ to be quoted). + +Finally, ODS2 enters interactive mode. Under Unix, with READLINE support, command +history is preserved in $HOME/.ods2. + +The HELP command provides full command syntax, which is very similar to DCL. +The help text is located in ods2.hlb, in the same directory as ODS2. You can +set the environment variable ODS2HELP to an alternate helpfile or use the +set helpfile command. Note that helpfiles are binary - see 00INSTALL.TXT.. + +N.B. The default help library filename is "ods2.hlb", in the same directory as +the ods2 executable. This file is made a symlink or copy of the default +language by the makefile. If another available language is desired, +softlink (or copy) the desired language file to it (or set the ODS2HELP + environment variable.) +E.g. for US English help: ln -s ods2_en_us.hlb ods2.hlb + +The following is a brief summary of commonly-used command sequences. +Abbreviations can be used. Full VMS filenames (device:[dir.sfd...]name.ext;gen) +can be used for files on the FILES-11 volume. Wildcards are supported for most +commands. Wildcards can be used on the host filesystem, e.g. when copying files +to the volume. + +The most important command for any program: +ODS2$> EXIT + +(QUIT also works.) + +Create a new Files-11 volume: + +ODS2$> INITIALIZE /medium:RM02/LOG rm02.vhd myvolume + +N.B. The .vhd file type will create a dynamic (sparse) VHD-format simulator file. + It is important to specify the medium type, as FILES-11 is sensitve to volume + size and geometry. + +Create a directory: +ODS2$> CREATE/DIRECTORY/LOG [FRED.PAKS] + +Copy files to the volume: +ODS2$> COPY /TO_FILES-11/ASCII *.txt *.doc [FRED.PAKS]*.* + +Set default directory on the volume: +ODS2$> SET DEFAULT [FRED.PAKS] + +See what's there: +ODS2$> DIR /DATE/SIZE + +Copy files from the volume: +ODS2$> COPY /FROM_FILES-11/ASCII *.txt *.* + +Mount an existing volume: +ODS2$> MOUNT /medium:RM02 /WRITE rmo2.vhd myvolume + +N.B. The volume name is optional, but if specified must match. + +Dismount the volume: +ODS2$> DISMOUNT A: +(The drive letter will vary - it's reported by INITIALIZE/MOUNT) + +N.B. It's VERY important to dismount the volume, especially if it's mounted + for write. ODS2 makes heavy use of caching, and any interruption will + leave it in an inconsistent state. + +Inspect a file on the volume: +ODS2$> TYPE/PAGE *.TXT + +Obtain information: +ODS2$> SHOW DEVICES +ODS2$> SHOW VOLUMES + +Interactively create a file: +ODS2$> CREATE FILE.TXT +mumble +gobble +^D +(or ^Z on Windows/VMS) + +Delete a file +ODS2$> DELETE FILE.TXT;2 + +More advanced usage: + +When asked for confirmation, the VMS DCL responses are accepted: + 0, no, false : Don't do this action, ask again for additional items + 1, yes, true : Proceed with this action, ask again for additional items + quit : Don't do this action, and don't consider any more items + all : Proceed with this action, and do all other items without asking + +Lines beginning with ';' or '!' are considered comments, and ignored. This +may be used for commenting command files. + +With editline (libedit) support, a comment entry is made in the command history +when an initialization file is opened. This can be used to trace initialization +issues. editlline also uses ~/.editrc and can be customized using the +'ods2' tag. editline is a Unix command history editor that can be compiled +with ods2. 'man 7 editline' and 'man 5 editrc' for usage details. editline +is available on most Linux distributions; building ods2 requires the corresponding +-dev(el) package. + +It may be necessary to quote command arguments, e.g. when specifying a +file name on a Unix system that includes '/'. Use '"' or "'"; double +the quote character to include it in the string. e.g. """hello""" produces + "hello" + +Alternatively, you can switch to unix-style options with SET QUALIFIER_STYLE UNIX. + +A heuristic is used to distinguish physical devices from simulator disk image +files in the mount and initialize commands. Use /device or /image if it +guesses wrong. + +It is possible to mount multiple volumes simultaneously, though not +(currently) to move files directly from one Files-11 volume to another. + +It is possible to mount volumesets, though this hasn't been completely debugged. + +Command files can be executed with @filename. They can be nested. + +Favorite directory and/or copy qualifiers can be set in the .ini file, e.g. +SET DIRECTORY /DATE/SIZE + +With VHD support, you can write to a snapshot of the volume: +ODS2$> MOUNT /SNAPSHOTS_OF=myvolume.vhd testvolume.vhd /write + +For more details on these and other capabilities, use the HELP command. + +For build/installation information, see 00INSTALL.TXT + +Bug reports, contributions, and feature requests are welcome +at https://github.com/open-simh/simtools/issues. + + +-- Timothe Litt + April 2016, October 2022 diff --git a/extracters/ods2/00_old_FAQ.txt b/extracters/ods2/00_old_FAQ.txt index 78161e1..4e72db1 100644 --- a/extracters/ods2/00_old_FAQ.txt +++ b/extracters/ods2/00_old_FAQ.txt @@ -1,175 +1,175 @@ - ODS2 June '98 - - A Program to Read ODS2 Disk Volumes - - -Say, what is this? - ODS2 is a program to read VMS disk volumes written in VMS - ODS2 format. - -Why bother? - Well sometimes it is convenient to see what is on a VMS CD, - or copy files from VMS disk on another platform. Maybe in - future it could be used as a basis for a PC Bookreader - program, or possibly other software? - -What other platforms? - ODS2 is written in 'Standard C' with the intent that it can - be compiled and run on non-VMS systems. However this requires - a C compiler which has 32 bit 'int' types, 16 bit 'short' - types, and 8 bit 'char'. The current version does no special - 'endian' handling so it can only be run on systems which are - little-endian like the VAX. Fortunately that inludes Intel... - -Could it be made to run on big-endian systems? - Yes it could! This would probably best be done by defining - C macros or functions to extract words or long words from the - disk structures as appropriate. On a little-endian system these - would be simply pass-through, while on a big-endian system they - would reverse the byte order. I have not attempted this because - I don't have a suitable test system!! - -What else is needed? - Some operating system support is also required to read an - absolute disk sector from the target disk. This is NOT as - easy as it sounds. I do not currently have sufficient - documentation to set this up for as many platforms as I - would like!! However I do have modules to read disks under - VMS (easy!) and floppies and CD under OS2, Windows 95 and - Windows NT. - -How do I build it? - On a VMS system ODS2 can be compiled by executing the - procedure BUILD.COM. On other platforms you need to compile - Ods2.c, Rms.c, Direct.c, Access.c, Device.c, Cache.c, Vmstime.c, - and the appropriate Phy*.c routine for your system. On OS/2 I - compile using the gcc command:- - gcc -fdollars-in-identifiers ods2.c,rms.c,direct.c, - access.c,device.c,cache.c,phyos2.c,vmstime.c - -What can it do? - Basically ODS2 provides cut down DIRECTORY, COPY and - SEARCH commands for VMS volumes on non-VMS systems. These - can be used to find out what is on a VMS volume, and copy - files onto the local file sytem. - -What file types? - Basically ODS2 can only deal with sequential files. I do not - have information on how indexed file types are constructed, - and relative files are of limited interest. - -What about volume sets? - ODS2 does contain support for multi-volume sets. However there - is no checking that the correct volumes have been specified and - error handling is very fragile. You should ensure that you - specify volume set mount commands very carefully! - -What about ODS5? - Sorry, but I have no idea! I have not seen any ODS5 information - or specifications. Most likely this program will fall in a heap - when presented with an ODS5 disk volume. - -What about bugs? - There are plenty!! This code has been tested for several hours - by a single developer/user on one workstation (absolutely no - testing or input from any other person up to this point!). - Contrast this to the VMS filesystem which has had hundreds of - developers, millions of users, and has run on about 500,000 - systems over the last 20 years!! I would hope to fix some of - the more severe limitations and provide better error handling - fairly soon, perhaps even put some comments into the code? - Maybe the next release might have fewer bugs? - -It is free? - Yeap! It is provided 'as is' to help people in the VMS - community. However there is NO SUPPORT! I will try to fix - any reported problems, but as I really only get to work on - it while the kids are at Scouts on Monday nights, fixes - happen slowly! But if you have comments or suggestions then - please feel free to mail me! - -Can I use the code? - Yeap! You can use and modify the code provided that you - acknowledge the author in the source of any modules which use - any part of this code. I would also appreciate, where possible, - being sent any enhancements for my own use. - -Can I call the routines from my program? - You should be able to. Many of the modules follow an 'RMS' - sort of standard, and programs which only ever read RMS files - from C might be converted to read VMS volumes on another platform - without too much effort. In addition to the RMSish interface, - it would not be difficult to package up an $ASSIGN/$QIOW interface - for common file operations... - -What is the status of ODS2? - This is the first release - actually more like a prototype - than an real release! But it may generate useful feedback and - possibly be useful to others the way it is? However if you are - tempted to use this version for real file transfers, DO YOUR OWN - TESTING first! This program may not have encountered volumes and - files like yours and could easily generate garbage results!!! - -Is more work happening? - Yes! I find the program very useful moving stuff from my - VAXstation to my PC. I would like to be able to write ODS2 volumes - so that I can move files in the other direction! And yes I hope - to generally improve the code - particularly in the area of error - handling! - -Can I make a suggestion? - You sure can! If you see something which needs improvement then - let me know. It may be more interesting than whatever I am doing now! - In fact if I don't hear from anyone then I might even loose interest! - -Can I see a command sample? - Sure:- - C:> ODS2 - ODS2 v1.2 - $> mount E:,F: - %MOUNT-I-MOUNTED, Volume FOX1 mounted on E: - %MOUNT-I-MOUNTED, Volume FOX2 mounted on F: - $> direct/file E:[*...] - Directory E:[PNANKERVIS] - 3MONTH.LOWUSE;1 (51,20,2) - ACCTEST.COM;1 (137,4,1) - ACCTEST.EXE;1 (53,4,2) - ..... - $> set default E:[sys0.sysmgr] - $> dir [-.sys*...].% - ..... - $> copy *.c *.* - %COPY-S-COPIED, E:[SYS0.SYSMGR]ACCESS.C;1 copied to ACCESS.C (3 records) - .... - $> show time - 24-MAR-1998 20:15:23.5 - $> dismount E: - $> exit - -What commands are supported? - A summary is:- - mount DRIVE:[,DRIVE:...] - directory [/file|/size|/date] [FILE-SPEC] - copy FILE-SPEC OUTPUT-FILE - dismount DRIVE: - search FILE-SPEC STRING - set default DIR-SPEC - show default - show time - exit - Note - DRIVE: is normally the native system drive name, - for example D: might be a CD on a PC - xxx: might be - /dev/xxx on a Unix system. - - when a list of drives is specified on the mount command - they are assumed to contain a single valid volume set - (this is not validated!!) - - file-spec is in the usual VMS syntax and may contain - wildcards (for example A:[-.*obj%%...]*abc*.obj;-2) - -Who would write this? - Me! Maybe it will become the basis of something more? If you - have suggestions or want to know more then please mail me at - paulnank@au1.ibm.com - - Thanks for listening! - Paul Nankervis + ODS2 June '98 + + A Program to Read ODS2 Disk Volumes + + +Say, what is this? + ODS2 is a program to read VMS disk volumes written in VMS + ODS2 format. + +Why bother? + Well sometimes it is convenient to see what is on a VMS CD, + or copy files from VMS disk on another platform. Maybe in + future it could be used as a basis for a PC Bookreader + program, or possibly other software? + +What other platforms? + ODS2 is written in 'Standard C' with the intent that it can + be compiled and run on non-VMS systems. However this requires + a C compiler which has 32 bit 'int' types, 16 bit 'short' + types, and 8 bit 'char'. The current version does no special + 'endian' handling so it can only be run on systems which are + little-endian like the VAX. Fortunately that inludes Intel... + +Could it be made to run on big-endian systems? + Yes it could! This would probably best be done by defining + C macros or functions to extract words or long words from the + disk structures as appropriate. On a little-endian system these + would be simply pass-through, while on a big-endian system they + would reverse the byte order. I have not attempted this because + I don't have a suitable test system!! + +What else is needed? + Some operating system support is also required to read an + absolute disk sector from the target disk. This is NOT as + easy as it sounds. I do not currently have sufficient + documentation to set this up for as many platforms as I + would like!! However I do have modules to read disks under + VMS (easy!) and floppies and CD under OS2, Windows 95 and + Windows NT. + +How do I build it? + On a VMS system ODS2 can be compiled by executing the + procedure BUILD.COM. On other platforms you need to compile + Ods2.c, Rms.c, Direct.c, Access.c, Device.c, Cache.c, Vmstime.c, + and the appropriate Phy*.c routine for your system. On OS/2 I + compile using the gcc command:- + gcc -fdollars-in-identifiers ods2.c,rms.c,direct.c, + access.c,device.c,cache.c,phyos2.c,vmstime.c + +What can it do? + Basically ODS2 provides cut down DIRECTORY, COPY and + SEARCH commands for VMS volumes on non-VMS systems. These + can be used to find out what is on a VMS volume, and copy + files onto the local file sytem. + +What file types? + Basically ODS2 can only deal with sequential files. I do not + have information on how indexed file types are constructed, + and relative files are of limited interest. + +What about volume sets? + ODS2 does contain support for multi-volume sets. However there + is no checking that the correct volumes have been specified and + error handling is very fragile. You should ensure that you + specify volume set mount commands very carefully! + +What about ODS5? + Sorry, but I have no idea! I have not seen any ODS5 information + or specifications. Most likely this program will fall in a heap + when presented with an ODS5 disk volume. + +What about bugs? + There are plenty!! This code has been tested for several hours + by a single developer/user on one workstation (absolutely no + testing or input from any other person up to this point!). + Contrast this to the VMS filesystem which has had hundreds of + developers, millions of users, and has run on about 500,000 + systems over the last 20 years!! I would hope to fix some of + the more severe limitations and provide better error handling + fairly soon, perhaps even put some comments into the code? + Maybe the next release might have fewer bugs? + +It is free? + Yeap! It is provided 'as is' to help people in the VMS + community. However there is NO SUPPORT! I will try to fix + any reported problems, but as I really only get to work on + it while the kids are at Scouts on Monday nights, fixes + happen slowly! But if you have comments or suggestions then + please feel free to mail me! + +Can I use the code? + Yeap! You can use and modify the code provided that you + acknowledge the author in the source of any modules which use + any part of this code. I would also appreciate, where possible, + being sent any enhancements for my own use. + +Can I call the routines from my program? + You should be able to. Many of the modules follow an 'RMS' + sort of standard, and programs which only ever read RMS files + from C might be converted to read VMS volumes on another platform + without too much effort. In addition to the RMSish interface, + it would not be difficult to package up an $ASSIGN/$QIOW interface + for common file operations... + +What is the status of ODS2? + This is the first release - actually more like a prototype + than an real release! But it may generate useful feedback and + possibly be useful to others the way it is? However if you are + tempted to use this version for real file transfers, DO YOUR OWN + TESTING first! This program may not have encountered volumes and + files like yours and could easily generate garbage results!!! + +Is more work happening? + Yes! I find the program very useful moving stuff from my + VAXstation to my PC. I would like to be able to write ODS2 volumes + so that I can move files in the other direction! And yes I hope + to generally improve the code - particularly in the area of error + handling! + +Can I make a suggestion? + You sure can! If you see something which needs improvement then + let me know. It may be more interesting than whatever I am doing now! + In fact if I don't hear from anyone then I might even loose interest! + +Can I see a command sample? + Sure:- + C:> ODS2 + ODS2 v1.2 + $> mount E:,F: + %MOUNT-I-MOUNTED, Volume FOX1 mounted on E: + %MOUNT-I-MOUNTED, Volume FOX2 mounted on F: + $> direct/file E:[*...] + Directory E:[PNANKERVIS] + 3MONTH.LOWUSE;1 (51,20,2) + ACCTEST.COM;1 (137,4,1) + ACCTEST.EXE;1 (53,4,2) + ..... + $> set default E:[sys0.sysmgr] + $> dir [-.sys*...].% + ..... + $> copy *.c *.* + %COPY-S-COPIED, E:[SYS0.SYSMGR]ACCESS.C;1 copied to ACCESS.C (3 records) + .... + $> show time + 24-MAR-1998 20:15:23.5 + $> dismount E: + $> exit + +What commands are supported? + A summary is:- + mount DRIVE:[,DRIVE:...] + directory [/file|/size|/date] [FILE-SPEC] + copy FILE-SPEC OUTPUT-FILE + dismount DRIVE: + search FILE-SPEC STRING + set default DIR-SPEC + show default + show time + exit + Note - DRIVE: is normally the native system drive name, + for example D: might be a CD on a PC - xxx: might be + /dev/xxx on a Unix system. + - when a list of drives is specified on the mount command + they are assumed to contain a single valid volume set + (this is not validated!!) + - file-spec is in the usual VMS syntax and may contain + wildcards (for example A:[-.*obj%%...]*abc*.obj;-2) + +Who would write this? + Me! Maybe it will become the basis of something more? If you + have suggestions or want to know more then please mail me at + paulnank@au1.ibm.com + + Thanks for listening! + Paul Nankervis diff --git a/extracters/ods2/Makefile b/extracters/ods2/Makefile index 684c52f..cf88e28 120000 --- a/extracters/ods2/Makefile +++ b/extracters/ods2/Makefile @@ -1 +1 @@ -makefile.unix \ No newline at end of file +makefile.unix diff --git a/extracters/ods2/access.c b/extracters/ods2/access.c index 663266d..422d3f6 100644 --- a/extracters/ods2/access.c +++ b/extracters/ods2/access.c @@ -1,1724 +1,1724 @@ -/* Access.c */ - -/* - * 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -/* - * This module implements 'accessing' files on an ODS2 - * disk volume. It uses its own low level interface to support - * 'higher level' APIs. For example it is called by the - * 'RMS' routines. - */ - -#if !defined( DEBUG ) && defined( DEBUG_ACCESS ) -#define DEBUG DEBUG_ACCCESS -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include -#include -#include -#include -#include - -#include "ssdef.h" -#include "access.h" -#include "device.h" -#include "initvol.h" -#include "ods2.h" -#include "phyvirt.h" -#include "stsdef.h" -#include "compat.h" -#include "sysmsg.h" -#include "vmstime.h" - -extern vmscond_t mountdef( const char *devnam ); - -struct VCB *vcb_list = NULL; - -/* WCBKEY passes info to compare/create routines */ - -struct WCBKEY { - unsigned vbn; - struct FCB *fcb; - struct WCB *prevwcb; -}; - -static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *scb ); - -#define DEBUGx - -/***************************************************************** checksum() */ - -/* checksum() to produce header checksum values... */ - -f11word checksumn( f11word *block, int count ) { - register unsigned result; - register unsigned short *ptr; - register unsigned data; - - ptr = block; - result = 0; - for ( ; count > 0; --count ) { - data = *ptr++; - result += F11WORD(data); - } - return (f11word)(result & 0xFFFF); -} - -f11word checksum( f11word *block ) { - return checksumn( block, 255 ); -} - -/***************************************************************** fid_copy() */ - -/* fid_copy() copy fid from file header with default rvn! */ - -void fid_copy( struct fiddef *dst, struct fiddef *src, unsigned rvn ) { - 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; -} - -/************************************************************* deaccesshead() */ - -/* deaccesshead() release header from INDEXF... */ - -vmscond_t deaccesshead( struct VIOC *vioc, struct HEAD *head, uint32_t idxblk ) { - f11word check; - - if ( head && idxblk ) { - check = checksum( (f11word *) head ); - head->fh2$w_checksum = F11WORD(check); - } - return deaccesschunk( vioc, idxblk, 1, TRUE ); -} - -/*************************************************************** accesshead() */ - -/* accesshead() find file or extension header from INDEXF... */ - -vmscond_t accesshead( struct VCB *vcb, struct fiddef *fid, unsigned seg_num, - struct VIOC **vioc, struct HEAD **headbuff, - uint32_t *retidxblk, unsigned wrtflg ) { - register vmscond_t sts; - register struct VCBDEV *vcbdev; - register uint32_t idxblk; - - vcbdev = RVN_TO_DEV(vcb, fid->fid$b_rvn); - if( vcbdev == NULL ) - return SS$_DEVNOTMOUNT; - - if( wrtflg && !(vcb->status & VCB_WRITE) ) - return SS$_WRITLCK; - - idxblk = fid->fid$w_num + - (fid->fid$b_nmx << 16) - 1 + - F11WORD(vcbdev->home.hm2$w_ibmapvbn) + - F11WORD(vcbdev->home.hm2$w_ibmapsize); - - if (vcbdev->idxfcb->head != NULL) { - if (idxblk >= - 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; - } - } - - if( $SUCCESSFUL(sts = accesschunk( vcbdev->idxfcb, idxblk, vioc, - (char **) headbuff, NULL, - wrtflg ? 1 : 0 )) ) { - register struct HEAD *head = *headbuff; - if( retidxblk ) { - *retidxblk = wrtflg ? idxblk : 0; - } - if (F11WORD(head->fh2$w_fid.fid$w_num) != fid->fid$w_num || - head->fh2$w_fid.fid$b_nmx != fid->fid$b_nmx || - 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)) { - sts = SS$_NOSUCHFILE; - } else if( head->fh2$b_idoffset < 38 || - head->fh2$b_idoffset > head->fh2$b_mpoffset || - head->fh2$b_mpoffset > head->fh2$b_acoffset || - head->fh2$b_acoffset > head->fh2$b_rsoffset || - head->fh2$b_map_inuse > - head->fh2$b_acoffset - head->fh2$b_mpoffset || - checksum( (f11word *) head ) != - F11WORD( head->fh2$w_checksum ) ) { -#if DEBUG - printf( "--->accesshead(): File header checksum failure:" ); - printf( " FH2$W_CHECKSUM=%u, checksum()=%u\n", - F11WORD( head->fh2$w_checksum ), - checksum( (f11word *) head ) - ); -#endif - sts = $SETLEVEL(SS$_BADCHKSUM, SEVERE); - } else if( F11WORD(head->fh2$w_seg_num) != seg_num ) { - sts = SS$_FILESEQCHK; - } - - if( $FAILED(sts) ) { - deaccesschunk( *vioc, 0, 0, FALSE ); - } - } - return sts; -} - -/************************************************************** wcb_compare() */ - -/* wcb_compare() compare two windows - return -1 for less, 0 for match */ - -/* as a by product keep highest previous entry so that if a new window - * is required we don't have to go right back to the initial file header - */ - -static int wcb_compare( uint32_t hashval, void *keyval, void *thiswcb ) { - - register struct WCBKEY *wcbkey; - register struct WCB *wcb; - - UNUSED(hashval); - - wcbkey = (struct WCBKEY *) keyval; - wcb = (struct WCB *) thiswcb; - - if( wcbkey->vbn < wcb->loblk ) { - return -1; /* Search key is less than this window maps */ - } - - if (wcbkey->vbn <= wcb->hiblk) { - return 0; /* Search key must be in this window */ - } - if (wcbkey->prevwcb == NULL) { - wcbkey->prevwcb = wcb; - } else { - if (wcb->loblk != 0 && wcb->hiblk > wcbkey->prevwcb->hiblk) { - wcbkey->prevwcb = wcb; - } - } - return 1; /* Search key is higher than this window... */ -} - -/************************************************************ premap_indexf() */ - -/* premap_indexf() called to physically read the header for indexf.sys - * so that indexf.sys can be mapped and read into virtual cache.. - */ - -struct HEAD *premap_indexf( struct FCB *fcb, vmscond_t *retsts ) { - struct HEAD *head; - struct VCBDEV *vcbdev; - int iderr; - - vcbdev = RVN_TO_DEV( fcb->vcb, fcb->rvn ); - if( vcbdev == NULL ) { - *retsts = SS$_DEVNOTMOUNT; - return NULL; - } - head = (struct HEAD *) malloc(sizeof(struct HEAD)); - if( head == NULL ) { - *retsts = SS$_INSFMEM; - return NULL; - } - - if( $FAILS(*retsts = virt_read( vcbdev->dev, - F11LONG( vcbdev->home.hm2$l_ibmaplbn ) + - F11WORD( vcbdev->home.hm2$w_ibmapsize ), - sizeof( struct HEAD ), (char *) head )) ) { - free( head ); - return NULL; - } - if( (iderr = (F11WORD(head->fh2$w_fid.fid$w_num) != FID$C_INDEXF || - head->fh2$w_fid.fid$b_nmx != 0 || - F11WORD(head->fh2$w_fid.fid$w_seq) != FID$C_INDEXF)) || - F11WORD(head->fh2$w_checksum) != checksum( (f11word *) head ) ) { -#if DEBUG - printf( "--->premap_indexf(): Index file header %s" ); - printf( " failure: FH2$W_CHECKSUM=%u, checksum()=%u\n", - (iderr? "File ID match" : "checksum"), - F11WORD( head->fh2$w_checksum ), - checksum( (f11word *) head ) - ); -#endif - *retsts = $SETLEVEL((iderr? SS$_FILENUMCHK: SS$_BADCHKSUM), SEVERE); - free( head ); - return NULL; - } - - return head; -} - -/*************************************************************** wcb_create() */ - -/* wcb_create() creates a window control block by reading appropriate - * file headers... - */ - -static void *wcb_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { - register struct WCB *wcb; - uint32_t curvbn; - unsigned extents = 0; - struct HEAD *head; - struct VIOC *vioc = NULL; - register struct WCBKEY *wcbkey; - - UNUSED(hashval); - - wcb = (struct WCB *) calloc( sizeof(struct WCB), 1 ); - - if( wcb == NULL ) { - *retsts = SS$_INSFMEM; - return NULL; - } - wcbkey = (struct WCBKEY *) keyval; - wcb->cache.objmanager = NULL; - wcb->cache.objtype = OBJTYPE_WCB; - - if( wcbkey->prevwcb == NULL ) { - curvbn = - wcb->loblk = 1; - wcb->hd_seg_num = 0; - head = wcbkey->fcb->head; - if( head == NULL ) { - head = premap_indexf( wcbkey->fcb, retsts ); - if (head == NULL) { - free( wcb ); - return NULL; - } - head->fh2$w_ext_fid.fid$w_num = 0; - head->fh2$w_ext_fid.fid$b_nmx = 0; - } - fid_copy(&wcb->hd_fid, &head->fh2$w_fid, wcbkey->fcb->rvn); - } else { - wcb->loblk = wcbkey->prevwcb->hiblk + 1; - curvbn = wcbkey->prevwcb->hd_basevbn; - wcb->hd_seg_num = wcbkey->prevwcb->hd_seg_num; - memcpy( &wcb->hd_fid, &wcbkey->prevwcb->hd_fid, sizeof(struct fiddef) ); - head = wcbkey->fcb->head; - } - while( TRUE ) { - register f11word *mp; - register f11word *me; - - wcb->hd_basevbn = curvbn; - - if( wcb->hd_seg_num != 0 ) { - if( $FAILS(*retsts = accesshead(wcbkey->fcb->vcb, &wcb->hd_fid, - wcb->hd_seg_num, &vioc, &head, NULL, 0)) ) { - free(wcb); - return NULL; - } - } - mp = (f11word *) head + head->fh2$b_mpoffset; - me = mp + head->fh2$b_map_inuse; - while( mp < me ) { - register uint32_t phylen, phyblk; - register f11word mp0 = 0; - - switch( ((mp0 = F11WORD(*mp)) >> 14) & 0x3 ) { - case 0: - phylen = 0; - mp++; - break; - case 1: - phylen = (mp0 & 0x00ff) + 1; - phyblk = ((mp0 & 0x3f00) << 8) | F11WORD(mp[1]); - mp += 2; - break; - case 2: - phylen = (mp0 & 0x3fff) + 1; - phyblk = (F11WORD(mp[2]) << 16) | F11WORD(mp[1]); - mp += 3; - break; - case 3: default: - phylen = ((mp0 & 0x3fff) << 16) + F11WORD(mp[1]) + 1; - phyblk = (F11WORD(mp[3]) << 16) | F11WORD(mp[2]); - mp += 4; - break; - } - curvbn += phylen; - if( phylen != 0 && curvbn > wcb->loblk ) { - wcb->phylen[extents] = phylen; - wcb->phyblk[extents] = phyblk; - wcb->rvn[extents] = wcb->hd_fid.fid$b_rvn; - if( ++extents >= EXTMAX) { - if( curvbn > wcbkey->vbn ) { - break; - } else { - extents = 0; - wcb->loblk = curvbn; - } - } - } - } - if( extents >= EXTMAX || - (F11WORD(head->fh2$w_ext_fid.fid$w_num) == 0 && - head->fh2$w_ext_fid.fid$b_nmx == 0) ) { - break; - } - { - register unsigned rvn; - wcb->hd_seg_num++; - rvn = wcb->hd_fid.fid$b_rvn; - fid_copy(&wcb->hd_fid, &head->fh2$w_ext_fid, rvn); - if( vioc != NULL ) { - deaccesshead(vioc, NULL, 0); - vioc = NULL; - } - } - } - if( vioc != NULL ) { - deaccesshead(vioc, NULL, 0); - vioc = NULL; - } else { - if( wcbkey->fcb->head == NULL ) - free(head); - } - wcb->hiblk = curvbn - 1; - wcb->extcount = extents; - *retsts = SS$_NORMAL; - if( curvbn <= wcbkey->vbn ) { - free(wcb); - wcb = NULL; -#if DEBUG - printf( "--->wcb_create(): curvbn (=%u) <= wcbkey->vbn (=%u)\n", - curvbn, wcbkey->vbn ); -#endif - *retsts = SS$_DATACHECK; - } - - return wcb; -} - -/**************************************************************** getwindow() */ - -/* getwindow() find a window to map VBN to LBN ... */ - -vmscond_t getwindow( struct FCB * fcb, uint32_t vbn, struct VCBDEV **devptr, - uint32_t *phyblk, uint32_t *phylen, struct fiddef *hdrfid, - unsigned *hdrseq ) { - vmscond_t sts; - struct WCB *wcb; - struct WCBKEY wcbkey; - register unsigned extent = 0; - register unsigned togo; - -#if DEBUG - printf("Accessing window for vbn %u, file (%u)\n", vbn, fcb->cache.hashval); -#endif - memset( &wcbkey, 0, sizeof( wcbkey ) ); - wcbkey.vbn = vbn; - wcbkey.fcb = fcb; - wcbkey.prevwcb = NULL; - wcb = cache_find( (void *) &fcb->wcb, 0, &wcbkey, &sts, wcb_compare, wcb_create ); - if( wcb == NULL ) - return sts; - - togo = vbn - wcb->loblk; - while( togo >= wcb->phylen[extent] ) { - togo -= wcb->phylen[extent]; - if( ++extent > wcb->extcount ) - return SS$_BUGCHECK; - } - *devptr = RVN_TO_DEV(fcb->vcb, wcb->rvn[extent]); - *phyblk = wcb->phyblk[extent] + togo; - *phylen = wcb->phylen[extent] - togo; - - if( hdrfid != NULL ) - memcpy(hdrfid, &wcb->hd_fid, sizeof(struct fiddef)); - - if( hdrseq != NULL ) - *hdrseq = wcb->hd_seg_num; -#if DEBUG - printf("Mapping vbn %u to %u (%u -> %u)[%u] file (%u)\n", - vbn, *phyblk, wcb->loblk, wcb->hiblk, wcb->hd_basevbn, - fcb->cache.hashval); -#endif - cache_untouch( &wcb->cache, TRUE ); - - if( *devptr == NULL ) - return SS$_DEVNOTMOUNT; - - return SS$_NORMAL; -} - -/************************************************************* vioc_manager() */ - -/* Object manager for VIOC objects:- if the object has been - * modified then we need to flush it to disk before we let - * the cache routines do anything to it... - */ - -void *vioc_manager( struct CACHE * cacheobj, int flushonly ) { - register struct VIOC *vioc; - - UNUSED(flushonly); - - vioc = (struct VIOC *) cacheobj; - - if( vioc->modmask != 0 ) { - register struct FCB *fcb = NULL; - register unsigned int length = VIOC_CHUNKSIZE; - register uint32_t curvbn = 0; - register char *address = NULL; - register unsigned modmask = 0; - - fcb = vioc->fcb; - curvbn = (size_t)vioc->cache.hashval + 1; - address = (char *) vioc->data; - modmask = vioc->modmask; - -#if DEBUG - printf("\nvioc_manager writing vbn %d\n", curvbn); -#endif - do { - register vmscond_t sts; - unsigned int wrtlen = 0; - uint32_t phyblk = 0, phylen = 0, idxlbn; - struct VCBDEV *vcbdev = NULL; - - while( length > 0 && !(1 & modmask) ) { - length--; - curvbn++; - address += 512; - modmask = modmask >> 1; - } - while (wrtlen < length && (1 & modmask) ) { - wrtlen++; - modmask = modmask >> 1; - } - length -= wrtlen; - while( wrtlen > 0 ) { - if( fcb->highwater != 0 && curvbn >= fcb->highwater ) { - length = 0; - break; - } - if( $FAILS(sts = getwindow(fcb, curvbn, &vcbdev, - &phyblk, &phylen, NULL, NULL)) ) - return NULL; - if (phylen > wrtlen) - phylen = wrtlen; - if (fcb->highwater != 0 && - curvbn + phylen > fcb->highwater) { - phylen = fcb->highwater - curvbn; - } - if( $FAILS(sts = virt_write( vcbdev->dev, phyblk, - phylen * 512, address )) ) - return NULL; - - idxlbn = (F11LONG(vcbdev->home.hm2$l_ibmaplbn) + - F11WORD(vcbdev->home.hm2$w_ibmapsize)); - - if( idxlbn >= phyblk && idxlbn < phyblk + phylen ) { - if( $FAILS(sts = virt_write( vcbdev->dev, - F11LONG(vcbdev->home.hm2$l_altidxlbn), - 512, address + - (512 * ((size_t)idxlbn - phyblk)) )) ) - return NULL; - } - wrtlen -= phylen; - curvbn += phylen; - address += (size_t)phylen * 512; - } - } while( length > 0 && modmask != 0 ); - vioc->modmask = 0; - vioc->cache.objmanager = NULL; - } - return cacheobj; -} - -/************************************************************ deaccesschunk() */ - -/* deaccesschunk() to deaccess a VIOC (chunk of a file) */ - -vmscond_t deaccesschunk( struct VIOC *vioc, uint32_t wrtvbn, - int wrtblks, int reuse ) { -#if DEBUG - printf( "Deaccess chunk %u (%08x) wrtvbn: %u blks: %u ru: %u\n", - vioc->cache.hashval, vioc->cache.hashval, wrtvbn, wrtblks, reuse ); -#endif - - if( wrtvbn ) { - register unsigned modmask; - - if( wrtvbn <= vioc->cache.hashval || - wrtvbn + wrtblks > vioc->cache.hashval + VIOC_CHUNKSIZE + 1 ) { -#if DEBUG - printf( "VBN out of reserved range\n" ); -#endif - return SS$_BADPARAM; - } - - modmask = 1 << (wrtvbn - vioc->cache.hashval - 1); - - while( --wrtblks > 0 ) - modmask |= modmask << 1; - - if( (vioc->wrtmask | modmask) != vioc->wrtmask ) { -#if DEBUG - printf( "Write mask error %08x %08x\n", - (vioc->wrtmask | modmask), vioc->wrtmask ); -#endif - return SS$_WRITLCK; - } - - vioc->modmask |= modmask; - if( vioc->cache.refcount == 1 ) - vioc->wrtmask = 0; - vioc->cache.objmanager = vioc_manager; - } - - cache_untouch( &vioc->cache, reuse ); - return SS$_NORMAL; -} - -/************************************************************** vioc_create() */ - -static void *vioc_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { - - register struct VIOC *vioc; - register uint32_t length; - register uint32_t curvbn; - register char *address; - register struct FCB *fcb; - - vioc = (struct VIOC *) calloc( 1, sizeof(struct VIOC) ); - if( vioc == NULL ) { - *retsts = SS$_INSFMEM; - return NULL; - } - - curvbn = hashval + 1; - fcb = (struct FCB *) keyval; - - vioc->cache.objmanager = NULL; - vioc->cache.objtype = OBJTYPE_VIOC; - vioc->fcb = fcb; - vioc->wrtmask = 0; - vioc->modmask = 0; - length = fcb->hiblock - curvbn + 1; - if( length > VIOC_CHUNKSIZE ) - length = VIOC_CHUNKSIZE; - address = (char *) vioc->data; - do { - register vmscond_t sts; - uint32_t phyblk = 0, phylen = 0; - struct VCBDEV *vcbdev = NULL; - - if (fcb->highwater != 0 && curvbn >= fcb->highwater) { - memset( address, 0, (size_t)length * 512 ); - break; - } - - if( $SUCCESSFUL(sts = getwindow( fcb, curvbn, &vcbdev, - &phyblk, &phylen, NULL, NULL )) ) { - if( phylen > length ) - phylen = length; - if( fcb->highwater != 0 && curvbn + phylen > fcb->highwater ) { - phylen = fcb->highwater - curvbn; - } - sts = virt_read( vcbdev->dev, phyblk, phylen * 512, - address ); - } - if( $FAILED(sts) ) { - free(vioc); - *retsts = sts; - return NULL; - } - length -= phylen; - curvbn += phylen; - address += (size_t)phylen * 512; - } while( length > 0 ); - *retsts = SS$_NORMAL; - - return vioc; -} - -/************************************************************** accesschunk() */ - -/* accesschunk() return pointer to a 'chunk' of a file ... */ - -vmscond_t accesschunk( struct FCB *fcb, uint32_t vbn, struct VIOC **retvioc, - char **retbuff, uint32_t *retblocks, unsigned wrtblks ) { - vmscond_t sts; - register uint32_t blocks; - register struct VIOC *vioc; - -#if DEBUG - printf("Access chunk %u (%u)\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 (vbn + wrtblks > blocks + VIOC_CHUNKSIZE + 1) { - return SS$_BADPARAM; - } - } - vioc = cache_find((void *) &fcb->vioc, blocks, fcb, &sts, NULL, vioc_create); - if (vioc == NULL) - return sts; - /* - Return result to caller... - */ - *retvioc = vioc; - blocks = vbn - blocks - 1; - *retbuff = vioc->data[blocks]; - if (wrtblks || retblocks != NULL) { - register unsigned modmask = 1 << blocks; - - blocks = VIOC_CHUNKSIZE - blocks; - if (vbn + blocks > fcb->hiblock) - blocks = fcb->hiblock - vbn + 1; - if (wrtblks && blocks > wrtblks) - blocks = wrtblks; - if (retblocks != NULL) - *retblocks = blocks; - if (wrtblks && blocks) { - if( fcb->highwater != 0 && vbn + blocks > fcb->highwater ) { - fcb->highwater = vbn + blocks; - fcb->head->fh2$l_highwater = F11LONG( fcb->highwater ); - } - while( --blocks > 0 ) - modmask |= modmask << 1; - vioc->wrtmask |= modmask; - vioc->cache.objmanager = vioc_manager; - } - } - return SS$_NORMAL; -} - -/************************************************************* deaccessfile() */ - -/* deaccessfile() finish accessing a file.... */ - -vmscond_t deaccessfile( struct FCB *fcb ) { -#if DEBUG - printf("Deaccessing file (%u) reference %d\n", fcb->cache.hashval, - fcb->cache.refcount); -#endif - if( fcb->cache.refcount == 1 ) { - register unsigned refcount; - - refcount = cache_refcount((struct CACHE *) fcb->vioc); - if( refcount && fcb->vioc == fcb->headvioc ) { - /* Index file header and data may share a VIOC. - * In this case, there's no way to know how - * many of the refs are from each source. Assume 1 from header. - */ - --refcount; - } - refcount += cache_refcount((struct CACHE *) fcb->wcb); - - if( refcount != 0 ) { -#if DEBUG - printf("File reference counts non-zero %d (%d)\n", refcount, - fcb->cache.hashval); -#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 ) && - (fcb->head->fh2$l_filechar & F11LONG(FH2$M_MARKDEL)) ) { - return deallocfile(fcb); - } - } - cache_untouch(&fcb->cache, TRUE); - return SS$_NORMAL; -} - -/************************************************************** fcb_manager() */ - -/* Object manager for FCB objects:- we point to one of our - sub-objects (vioc or wcb) in preference to letting the - cache routines get us! But we when run out of excuses - it is time to clean up the file header... :-( */ - -void *fcb_manager(struct CACHE *cacheobj, int flushonly) { - register struct FCB *fcb; - - fcb = (struct FCB *) cacheobj; - if( fcb->vioc != NULL ) - return &fcb->vioc->cache; - if( fcb->wcb != NULL ) - return &fcb->wcb->cache; - if( fcb->cache.refcount != 0 || flushonly) - return NULL; - if( fcb->headvioc != NULL ) { - deaccesshead(fcb->headvioc, fcb->head, fcb->headvbn); - fcb->headvioc = NULL; - } - return cacheobj; -} - -/*************************************************************** fcb_create() */ - -static void *fcb_create( uint32_t filenum, void *keyval, vmscond_t *retsts ) { - - register struct FCB *fcb; - - fcb = (struct FCB *) calloc( 1, sizeof(struct FCB) ); - - UNUSED(filenum); - UNUSED(keyval); - - if (fcb == NULL) { - *retsts = SS$_INSFMEM; - } else { - fcb->cache.objmanager = fcb_manager; - fcb->cache.objtype = OBJTYPE_FCB; - fcb->hiblock = 100000; - } - return fcb; -} - -/*************************************************************** accessfile() */ - -/* accessfile() open up file for access... */ - -vmscond_t accessfile( struct VCB * vcb, struct fiddef * fid, struct FCB **fcbadd, - unsigned wrtflg ) { - vmscond_t sts; - register struct FCB *fcb; - register uint32_t filenum; - - filenum = (fid->fid$b_nmx << 16) + fid->fid$w_num; -#if DEBUG - printf("Accessing file (%d,%d,%d) wrt %u\n", (fid->fid$b_nmx << 16) + - fid->fid$w_num, fid->fid$w_seq, fid->fid$b_rvn, wrtflg); -#endif - if( filenum < 1 ) - return SS$_BADPARAM; - 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; - /* If not found make one... */ - *fcbadd = fcb; - if( fcb->vcb == NULL ) { - fcb->rvn = fid->fid$b_rvn; - if( fcb->rvn == 0 && vcb->devices > 1 ) - fcb->rvn = 1; - fcb->vcb = vcb; - } - if( wrtflg ) { - if( fcb->headvioc != NULL && !(fcb->status & FCB_WRITE) ) { - deaccesshead(fcb->headvioc, NULL, 0); - fcb->headvioc = NULL; - } - fcb->status |= FCB_WRITE; - } - if( fcb->headvioc == NULL ) { - register vmscond_t sts; - - if( $SUCCESSFUL(sts = accesshead(vcb, fid, 0, &fcb->headvioc, - &fcb->head, &fcb->headvbn, - wrtflg)) ) { - fcb->hiblock = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); - if (fcb->head->fh2$b_idoffset > 39) { - fcb->highwater = F11LONG(fcb->head->fh2$l_highwater); - } else { - fcb->highwater = 0; - } - } else { -#if DEBUG - printf("Accessfile fail status %08x\n", sts); -#endif - fcb->cache.objmanager = NULL; - cache_untouch(&fcb->cache, TRUE); - cache_delete(&fcb->cache); - return sts; - } - } - return SS$_NORMAL; -} - -/***************************************************************** dismount() */ - -/* dismount() finish processing on a volume */ - -vmscond_t dismount (struct VCB * vcb, options_t options ) { - register vmscond_t sts, device; - int expectfiles; - int openfiles; - struct VCBDEV *vcbdev; - - expectfiles = vcb->devices; - openfiles = cache_refcount( &vcb->fcb->cache ); - - if( vcb->status & VCB_WRITE ) - expectfiles *= 2; -#if DEBUG - printf("Dismounting disk %d\n", openfiles); -#endif - sts = SS$_NORMAL; - if( openfiles != expectfiles ) { - printmsg( DISM_CANNOTDMT, 0, - 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? - vcb->vcbdev[0].home.hm2$t_strucname: - vcb->vcbdev[0].home.hm2$t_volname) ) ); - return printmsg( DISM_USERFILES, MSG_CONTINUE, - DISM_CANNOTDMT, openfiles - expectfiles ); - } - - vcbdev = vcb->vcbdev; - for( device = 0; device < vcb->devices; device++, vcbdev++ ) { -#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 ); -#if DEBUG - printf( "--->dismount(): %s = deaccessfile(bitmap)\n", getmsg(sts, 0) ); -#endif - if( $FAILED(sts) ) { - printmsg( DISM_CANNOTDMT, 0, 12, vcbdev->home.hm2$t_volname ); - sts = printmsg( sts, MSG_CONTINUE, DISM_CANNOTDMT ); - break; - } - vcbdev->mapfcb->status &= ~FCB_WRITE; - vcbdev->mapfcb = NULL; - } - - while (vcb->dircache) { - cache_delete((struct CACHE *) vcb->dircache); -#if DEBUG - printf( "--->dismount(): cache_delete(dircache)\n" ); -#endif - } - /* Remove all unreferenced fcbs - the LRU list still has references - * on file headers. This reduces the references to just the index file itself. - */ - cache_remove( &vcb->fcb->cache ); -#if DEBUG - printf( "--->dismount(): cache_remove()\n" ); -#endif - - /* Index file data & header. Special handling due to header also being - * file data for the index file. - */ - deaccesshead( vcbdev->idxfcb->headvioc, - vcbdev->idxfcb->head, vcbdev->idxfcb->headvbn ); - /* Check status? */ - vcbdev->idxfcb->headvioc = NULL; - cache_untouch(&vcbdev->idxfcb->cache, FALSE); - vcbdev->idxfcb->status &= ~FCB_WRITE; - vcbdev->idxfcb = NULL; - /* Remove index fcb, which should be the last in the FCB cache. */ - - cache_remove( &vcb->fcb->cache ); -#if DEBUG - printf( "--->dismount(): %s = deaccess(index)\n", getmsg(sts, 0) ); -#endif - if( $FAILED(sts) ) - break; - } - } - if( $SUCCESSFUL(sts) ) { - struct VCB *lp; - - vcbdev = vcb->vcbdev; - for (device = 0; device < vcb->devices; device++, vcbdev++) { - if( vcbdev->dev != NULL ) { -#if DEBUG - printf( "--->dismount(): virt_close(%s)\n", - vcbdev->dev->devnam ); -#endif - virt_close( vcbdev->dev ); - vcbdev->dev = NULL; - if( options & MOU_LOG ) - sts = printmsg( (vcb->devices > 1)? - DISM_DISMOUNTD: DISM_DISMOUNTDV, - 0, - 12, vcbdev->home.hm2$t_volname, - 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? - vcb->vcbdev[0].home.hm2$t_strucname: - vcb->vcbdev[0].home.hm2$t_volname) ) ); - } - } - for( lp = (struct VCB *)&vcb_list; lp->next != NULL; lp = lp->next ) { - if( lp->next == vcb ) { - lp->next = vcb->next; - break; - } - } - if( vcb->devices > 1 ) - sts = printmsg( DISM_DISMAL, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); - free(vcb); - } - - return sts; -} - -/******************************************************************** mount() */ - -#if DEBUG -#ifndef HOME_SKIP -#define HOME_SKIP 1 -#endif -#ifndef HOME_LIMIT -#define HOME_LIMIT 3 -#endif -#else -#ifndef HOME_SKIP -#define HOME_SKIP 0 -#endif -#ifndef HOME_LIMIT -#define HOME_LIMIT 1000 -#endif -#endif - -/* mount() make disk volume available for processing... */ - -vmscond_t mount( options_t flags, - unsigned devices, - char *devnam[], - char *label[] ) { - register unsigned device; - vmscond_t sts = 0; - struct VCB *vcb; - struct VCBDEV *vcbdev; - struct VOLSETREC *volsets = NULL; - -#if DEBUG - if (sizeof(struct HOME) != 512 || sizeof(struct HEAD) != 512) - return SS$_BUGCHECK; -#endif - - /* calloc() initializes most of the structure */ - - vcb = (struct VCB *) calloc( 1, sizeof(struct VCB) + - (((size_t)devices - 1) * sizeof(struct VCBDEV)) ); - if( vcb == NULL ) - return SS$_INSFMEM; - - if( flags & MOU_WRITE ) - vcb->status |= VCB_WRITE; - - /* Pass 1: validate device name & open each for I/O. - * Read HOM blocks & verify labels. - */ - vcbdev = vcb->vcbdev; - for( device = 0; device < devices; device++, vcbdev++ ) { - char *dname; - uint32_t errcnt = 0; - - dname = devnam[device]; - sts = MOUNT_FAILED; - vcbdev->dev = NULL; - if( *dname != '\0' ) { /* Really want to allow skipping volumes? */ - unsigned int hba, delta, homtry; - - if( label[device] != NULL && strlen(label[device]) > - sizeof( volsets[0].vsr$t_label ) ) { - sts = printmsg( MOUNT_BADLABEL, 0, label[device] ); - break; - } - if( $FAILS(sts = virt_open( &dname, flags, &vcbdev->dev )) ) - break; - vcb->devices++; - if (vcbdev->dev->vcb != NULL) { - sts = printmsg( SS$_DEVMOUNT, 0 ); - break; - } - if( (flags & MOU_WRITE ) && !(vcbdev->dev->access & MOU_WRITE) ) { - sts = printmsg( MOUNT_WRITELCK, 0, devnam[device] ); - vcb->status &= ~VCB_WRITE; - } - - delta = delta_from_index( (vcbdev->dev->access & MOU_DEVTYPE) >> MOU_V_DEVTYPE ); - - for( hba = 1, homtry = 0; - homtry < HOME_LIMIT; homtry++, hba += delta ) { - struct HOME *hom; - -#if HOME_SKIP > 100 - if( homtry < HOME_SKIP ) - continue; -#endif - if( $FAILS(sts = virt_read( vcbdev->dev, hba, - sizeof( struct HOME ), - (char *) &vcbdev->home )) ) - break; - hom = &vcbdev->home; -#if DEBUG || defined( HOME_LOG ) - printf( "--->mount(%u): LBA=%u, HM2$L_HOMELBN=%u, " - "HM2$L_ALHOMELBN=%u, " - "HM2$T_FORMAT=\"%12.12s\", memcmp()=%d,%d\n", - homtry+1, hba, F11LONG( hom->hm2$l_homelbn ), - F11LONG( hom->hm2$l_alhomelbn ), - hom->hm2$t_format, - memcmp( hom->hm2$t_format, "DECFILE11B ", 12 ), - memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) - ); -#endif - if( (hba == F11LONG(hom->hm2$l_homelbn)) && - ((F11LONG(hom->hm2$l_alhomelbn) != 0) || - (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 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) )) && - (F11WORD(hom->hm2$w_checksum2) == - checksum( (f11word *)hom )) && - ((memcmp(hom->hm2$t_format, "DECFILE11B ", 12) == 0) || - (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 0)) ) { - break; - } - errcnt++; - sts = MOUNT_HOMBLKCHK; -#if DEBUG || defined( HOME_LOG ) - printf( "--->mount(): Home block validation failure\n" ); - 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( "(F11WORD(hom->hm2$w_checksum2) = %u %u\n", F11WORD(hom->hm2$w_checksum2), - checksum( (f11word *)hom )); -#endif - } - if( $FAILED(sts) ) { - sts = printmsg( sts, 0, errcnt ); - break; - } - if( (F11WORD(vcbdev->home.hm2$w_struclev) >> 8) != 2 || - (F11WORD(vcbdev->home.hm2$w_struclev) & 0xFFu) < 1 ) { - sts = printmsg( sts, 0, - F11WORD(vcbdev->home.hm2$w_struclev) >> 8, - F11WORD(vcbdev->home.hm2$w_struclev) & 0XFFu ); - break; - } - if( errcnt && (flags & MOU_LOG) ) /* Suppress warning if /nolog for scripted mounts */ - printmsg( $SETLEVEL(MOUNT_HOMBLKCHK, WARNING), 0, errcnt ); - - if( label[device] != NULL ) { - int i; - char lbl[12+1]; /* Pad CLI-supplied label to match ODS */ - - (void) snprintf( lbl, sizeof(lbl), "%-12s", label[device] ); - - for( i = 0; i < 12; i++ ) { - if( toupper( lbl[i] ) != - toupper( vcbdev->home.hm2$t_volname[i] ) ) { - sts = printmsg( MOUNT_WRONGVOL, 0, dname, - 12, vcbdev->home.hm2$t_volname, lbl ); - break; - } - } - if( $FAILED(sts) ) - break; - } - if( (F11WORD(vcbdev->home.hm2$w_rvn) != device + 1) && - !(F11WORD(vcbdev->home.hm2$w_rvn) == 0 && device == 0) ) { - if( vcbdev->home.hm2$w_rvn == 0 ) { - sts = printmsg( MOUNT_NOTVSMEM, 0, dname, - 12, vcbdev->home.hm2$t_volname, - device+1, - 12, vcb->vcbdev[0].home.hm2$t_strucname ); - } else { - sts = printmsg( MOUNT_WRONGRVN, 0, dname, - 12, vcbdev->home.hm2$t_volname, - F11WORD(vcbdev->home.hm2$w_rvn), - 12, vcbdev->home.hm2$t_strucname, - device + 1, - 12, vcb->vcbdev[0].home.hm2$t_strucname ); - } - } - if( $FAILED(sts) ) - break; - } /* for(each device) */ - } - if( $FAILED(sts) ) { - vcbdev = vcb->vcbdev; - for( device = 0; device < vcb->devices; device++, vcbdev++ ) { - if (vcbdev->dev == NULL) { - continue; - } - virt_close( vcbdev->dev ); - } - free( vcb ); - vcb = NULL; - } else { - /* Pass 2: Open the index file. - * For RVN 1 of a volume set, read VOLSET.SYS. - * For writable volumes, open and validate BITMAP.SYS. - */ - vcbdev = vcb->vcbdev; - for( device = 0; device < devices; device++, vcbdev++ ) { - char *dname; - - dname = devnam[device]; - sts = MOUNT_FAILED; - vcbdev->idxfcb = NULL; - vcbdev->mapfcb = NULL; - vcbdev->clustersize = 0; - vcbdev->max_cluster = 0; - vcbdev->free_clusters = 0; - - if( *dname != '\0' ) { - struct fiddef idxfid = { FID$C_INDEXF, FID$C_INDEXF, 0, 0 }; - idxfid.fid$b_rvn = device + 1; - - if( !(vcb->status & ~VCB_WRITE) ) - vcbdev->dev->access &= ~MOU_WRITE; - - if( $FAILS(sts = accessfile( vcb, &idxfid, &vcbdev->idxfcb, - (vcb->status & VCB_WRITE) != 0 )) ) { - virt_close( vcbdev->dev ); - vcbdev->dev = NULL; - break; - } - vcbdev->dev->vcb = vcb; - if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) { - if( device == 0 ) { - struct fiddef vsfid = { FID$C_VOLSET, - FID$C_VOLSET, 1, 0 } ; - struct FCB *vsfcb = NULL; - struct VIOC *vioc = NULL; - unsigned rec; - uint32_t vbn = 1; - struct VOLSETREC *bufp = NULL; - - unsigned setcount; - - setcount = F11WORD(vcbdev->home.hm2$w_setcount); - if( setcount != devices ) { - sts = printmsg( MOUNT_WRONGVOLCNT, 0, - 12, vcbdev->home.hm2$t_strucname, - setcount, devices ); - break; - } - /* Read VOLSET.SYS - * 1 record for volset name + 1 for each member's label. - */ - volsets = - (struct VOLSETREC *)malloc( (1+(size_t)setcount) * - sizeof( struct VOLSETREC ) ); - if( volsets == NULL ) { - printmsg( MOUNT_VOLSETSYS, 0, dname ); - sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, MOUNT_VOLSETSYS ); - break; - } - if( $FAILS(sts = accessfile( vcb, &vsfid, &vsfcb, 0 )) ) { - printmsg( MOUNT_VOLSETSYS, 0, dname ); - sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); - break; - } - for( rec = 0; rec <= setcount; ) { - uint32_t blocks = 0, recs; - - if( $FAILS(sts = accesschunk( vsfcb, vbn, &vioc, - (char **)&bufp, - &blocks, 0 )) ) - break; - vbn += blocks; - - recs = (size_t)blocks * 512 / sizeof( struct VOLSETREC ); - if( rec + recs > setcount + 1 ) - recs = setcount + 1 - rec; - - memcpy(volsets+rec, bufp, recs * - sizeof( struct VOLSETREC )); - rec += recs; - - deaccesschunk( vioc, 0, 0, 0 ); - } - - { vmscond_t st2; - st2 = deaccessfile( vsfcb ); - if( $SUCCESSFUL(sts) ) - sts = st2; - } - if( $FAILED(sts) ) { - printmsg( MOUNT_VOLSETSYS, 0, dname ); - sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); - break; - } - if( memcmp(vcbdev->home.hm2$t_strucname, - volsets[0].vsr$t_label, 12 ) != 0 ) { - - sts = printmsg( MOUNT_WRONGVOLMEM, 0, - dname, - 12, vcbdev->home.hm2$t_strucname, - 12, volsets[0].vsr$t_label ); - break; - } - } else { /* device != 0 */ - if( vcb->vcbdev[0].dev == NULL ) { - sts = printmsg( MOUNT_RVN1NOTMNT, 0 ); - break; - } - if( memcmp(vcbdev->home.hm2$t_strucname, - vcb->vcbdev[0].home.hm2$t_strucname, - 12) != 0 ) { - sts = printmsg( MOUNT_INCONVOL, 0, - 12, vcbdev->home.hm2$t_volname, - dname, - 12, vcbdev->home.hm2$t_strucname, - 12, vcb->vcbdev[0].home.hm2$t_strucname ); - break; - } - } - /* All volset members */ - - if( memcmp(vcbdev->home.hm2$t_volname, - volsets[device+1].vsr$t_label, 12 ) != 0 ) { - sts = printmsg( MOUNT_VOLOOO, 0, device+1, - 12, vcb->vcbdev[0].home.hm2$t_strucname, - 12, volsets[device+1].vsr$t_label, - dname, - 12, vcbdev->home.hm2$t_volname ); - break; - } - } /* rvn != 0 */ - - /* All volumes */ - - if( vcb->status & VCB_WRITE ) { -#if DEBUG - struct SCB scb; - if( $FAILS(sts = get_scb( vcb, device, &scb )) ) - break; - - 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 ); -#else - if( $FAILS(sts = get_scb( vcb, device, NULL )) ) - break; -#endif - } - if( $SUCCESSFUL(sts) && (flags & MOU_LOG) ) { - sts = printmsg( MOUNT_MOUNTED, 0, - 12, vcbdev->home.hm2$t_volname, vcbdev->dev->devnam ); - } - } /* device len */ - } /* for( each device ) */ - if( $FAILED(sts) ) { /* Dismount any volumes prior to the error */ - vcbdev = vcb->vcbdev; - for( device = 0; device < devices; device++, vcbdev++ ) { - if (vcbdev->dev == NULL) { - continue; - } - - if( vcb->status & VCB_WRITE && vcbdev->mapfcb != NULL ) { - /* check status? */ - deaccessfile(vcbdev->mapfcb); - vcbdev->idxfcb->status &= ~FCB_WRITE; - vcbdev->mapfcb = NULL; - } - while( vcb->dircache ) - cache_delete( (struct CACHE *) vcb->dircache ); - cache_remove( &vcb->fcb->cache ); - if( vcbdev->idxfcb != NULL ) { - /* check status? = */ - deaccesshead( vcbdev->idxfcb->headvioc, - vcbdev->idxfcb->head, - vcbdev->idxfcb->headvbn ); - vcbdev->idxfcb->headvioc = NULL; - cache_untouch( &vcbdev->idxfcb->cache, FALSE ); - vcbdev->idxfcb->status &= ~FCB_WRITE; - vcbdev->idxfcb = NULL; - } - vcbdev->dev->vcb = NULL; - virt_close( vcbdev->dev ); - } - cache_remove( &vcb->fcb->cache ); - } - } - - /* Wrap up */ - - if( $SUCCESSFUL(sts) && (flags & MOU_LOG) && - F11WORD(vcb->vcbdev[0].home.hm2$w_rvn) != 0 ) { - sts = printmsg ( MOUNT_VSMOUNTED, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); - } - if( vcb != NULL ) { - struct VCB *lp; - - if( $SUCCESSFUL(sts) ) { - for( lp = (struct VCB *)&vcb_list; - lp->next != NULL; lp = lp->next ) - ; - lp->next = vcb; - vcb->next = NULL; - } else { - free( vcb ); - vcb = NULL; - } - } - if( volsets != NULL ) - free( volsets ); - - if( $SUCCESSFUL(sts) ) - (void) mountdef( vcb->vcbdev[0].dev->devnam ); - - return sts; -} - -/***************************************************************** get_scb() */ -static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *retscb ) { - vmscond_t sts; - int mapopen = 1; - struct fiddef mapfid = { FID$C_BITMAP, FID$C_BITMAP, 0, 0 }; - struct VCBDEV *vcbdev = NULL; - struct VIOC *vioc = NULL; - struct SCB *scb = NULL; - int volwrt; - - if( retscb != NULL ) - memset( retscb, 0, sizeof( struct SCB ) ); - - vcbdev = vcb->vcbdev + device; - - mapfid.fid$b_rvn = (f11byte) F11WORD(vcbdev->home.hm2$w_rvn); - - volwrt = (vcb->status & VCB_WRITE) != 0; - - if( vcbdev->mapfcb == NULL ) { - mapopen = 0; - - if( $FAILS(sts = accessfile( vcb, &mapfid, - &vcbdev->mapfcb, volwrt )) ) { - vcb->status &= ~VCB_WRITE; - vcbdev->mapfcb = NULL; - printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); - return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); - } - } - - if( $FAILS(sts = accesschunk( vcbdev->mapfcb, 1, &vioc, - (char **) &scb, NULL, 0 )) ) { - if( !mapopen ) { - deaccessfile( vcbdev->mapfcb ); - vcbdev->mapfcb = NULL; - vcb->status &= ~VCB_WRITE; - } - printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); - return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); - } - - if( retscb != NULL ) - memcpy( retscb, scb, sizeof( struct SCB ) ); - - if( (F11WORD(scb->scb$w_checksum) != checksum( (f11word *)scb )) || - (scb->scb$w_cluster != vcbdev->home.hm2$w_cluster) ) { - deaccesschunk(vioc, 0, 0, FALSE); - if( !mapopen ) { - deaccessfile( vcbdev->mapfcb ); - vcbdev->mapfcb = NULL; - vcb->status &= ~VCB_WRITE; - } - printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); - return printmsg( MOUNT_BADSCB, MSG_CONTINUE, MOUNT_BITMAPSYS ); - } - - 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) ) -1; - deaccesschunk(vioc, 0, 0, FALSE); - - if( !volwrt || !mapopen ) { - if( $FAILS(sts = update_freecount( vcbdev, - &vcbdev->free_clusters )) ) { - printmsg( MOUNT_BITUPDFAIL, 0, vcbdev->dev->devnam ); - sts = printmsg( sts, MSG_CONTINUE, MOUNT_BITUPDFAIL ); - } - if( !(mapopen || volwrt) ) { - deaccessfile( vcbdev->mapfcb ); - vcbdev->mapfcb = NULL; - } - } - - return sts; -} - -/***************************************************************** show_volumes */ -/* First, some private helper routines */ - -/********************************************************** print_volhdr */ -static void print_volhdr( int volset, size_t devwid ) { - size_t i; - - if( volset ) - printf( " RVN " ); - else - printf( " " ); - printf( -"%-*s Volume/User Lvl Clust Owner/CreateDate VolProt/Default FileProt\n", - (int)devwid, "Dev" ); - if( volset ) - printf( " --- " ); - else - printf( " " ); - - for( i= 0; i < devwid; i++ ) - putchar( '-' ); - printf( -" ------------ --- ----- ----------------- ---------------------------\n" ); - - return; -} - -/********************************************************** print_prot() */ -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; - - w = 27; - - for( g = 0; g < 4; g++ ) { - w -= printf( "%c:", grp[g] ); - for( b = 0; b < 4; b++ ) { - if( !(code & (1<<(b + (4*g)))) ) { - putchar( acc[vol][b] ); - w--; - } - } - if( g < 3 ) { - putchar( ',' ); - w--; - } - } - while( w-- ) putchar( ' ' ); -} - -/********************************************************** print_volinf() */ -static void print_volinf( struct VCB *vcb, - size_t devwid, - unsigned device, - size_t wrap ) { - struct VCBDEV *vcbdev; - struct SCB scb; - vmscond_t sts; - size_t n; - f11word timbuf[7]; - char uicbuf[ 1+6+1+6+1+1], *p; - - vcbdev = vcb->vcbdev+device; - - if( $FAILS(sts = get_scb( vcb, device, &scb )) ) { - if( $MATCHCOND(sts, SS$_BADPARAM) ) - printf(" SCB is invalid\n" ); - else - printf( " *Unable to read SCB: %s\n", getmsg( sts, MSG_TEXT ) ); - } - - for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) { - if( vcbdev->dev->devnam[n] == ':' ) - break; - putchar( vcbdev->dev->devnam[n] ); - } - printf( "%*s ", (int)(devwid-n), "" ); - - printf( "%12.12s", vcbdev->home.hm2$t_volname ); - - (void) snprintf(uicbuf, sizeof(uicbuf), "[%6o,%6o]", - F11WORD( vcbdev->home.hm2$w_volowner.uic$w_grp ), - F11WORD( vcbdev->home.hm2$w_volowner.uic$w_mem )); - for( p = uicbuf; p[1] == ' '; p++ ) { - p[0] = ' '; p[1] = '['; - } - p = strchr( uicbuf, ',' ); - n = strspn( ++p, " " ); - memmove( p, p + n, 7 - n ); - memset( p + 7 - n, ' ', n ); - - printf( " %u.%u %5u %s", - F11WORD( vcbdev->home.hm2$w_struclev ) >> 8, - F11WORD( vcbdev->home.hm2$w_struclev ) & 0xFF, - F11WORD( vcbdev->home.hm2$w_cluster ), - uicbuf - ); - printf( " " ); - print_prot( F11WORD( vcbdev->home.hm2$w_protect ), 1 ); - if( !(vcb->status & VCB_WRITE) ) - printf( " write-locked" ); - - if( $SUCCESSFUL(sts) ) { - 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( $SUCCESSFUL(sys_numtim( timbuf, vcbdev->home.hm2$q_credate )) ) { - static const char *months = - "-JAN-FEB-MAR-APR-MAY-JUN-JUL-AUG-SEP-OCT-NOV-DEC-"; - - printf( "%2u%.5s%4u %2u:%2u ", timbuf[2], - months+(4*((size_t)timbuf[1]-1)), timbuf[0], - timbuf[3], timbuf[4]); - } else - printf( "%*s", 41-23, "" ); - - print_prot( F11WORD( vcbdev->home.hm2$w_fileprot ), 0 ); - - if( $SUCCESSFUL(sts) ) { - 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' ); -} - -/********************************************************** show_volumes() */ -void show_volumes( void ) { - struct VCB *vcb; - size_t maxd = sizeof( "Dev" ) -1; - int nvol = 0; - unsigned device; - - if( vcb_list == NULL ) { - printf( " No volumes mounted\n" ); - return; - } - - for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { - struct VCBDEV *vcbdev; - - if( vcb->devices == 0 ) { - printf( "No devices for volume\n" ); - abort(); - } - nvol++; - vcbdev = vcb->vcbdev; - for( device = 0; device < vcb->devices; device++, vcbdev++ ) { - size_t n; - for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) - if( vcbdev->dev->devnam[n] == ':' ) - break; - if( n > maxd ) - maxd = n; - } - } - for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { - struct VCBDEV *vcbdev; - unsigned device; - - vcbdev = vcb->vcbdev; - if( F11WORD(vcbdev->home.hm2$w_rvn) == 0 ) - continue; - - nvol--; - printf( " Volume set %12.12s\n", vcbdev->home.hm2$t_strucname ); - - print_volhdr( TRUE, maxd ); - - for( device = 0; device < vcb->devices; device++, vcbdev++ ) { - printf( " %3d ", F11WORD(vcbdev->home.hm2$w_rvn) ); - print_volinf( vcb, maxd, device, 8 ); - } - } - if( nvol == 0 ) - return; - - printf( "\n" ); - - print_volhdr( FALSE, maxd ); - for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { - struct VCBDEV *vcbdev; - unsigned device; - - vcbdev = vcb->vcbdev; - if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) - continue; - - vcbdev = vcb->vcbdev; - for( device = 0; device < vcb->devices; device++, vcbdev++ ) { - printf( " " ); - print_volinf( vcb, maxd, device, 2 ); - } - } - - return; -} - -/*************************************************************** acccess_rundown() */ -void access_rundown( void ) { - struct VCB *vcb, *next = NULL; - vmscond_t sts; - - for( vcb = vcb_list; vcb != NULL; vcb = next ) { - next = vcb->next; - - if( $FAILS(sts = dismount( vcb, 0 )) ) { - printf( "Dismount failed in rundown: %s\n", getmsg(sts, MSG_TEXT) ); - } - } -} - +/* Access.c */ + +/* + * 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +/* + * This module implements 'accessing' files on an ODS2 + * disk volume. It uses its own low level interface to support + * 'higher level' APIs. For example it is called by the + * 'RMS' routines. + */ + +#if !defined( DEBUG ) && defined( DEBUG_ACCESS ) +#define DEBUG DEBUG_ACCCESS +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include +#include +#include +#include +#include + +#include "ssdef.h" +#include "access.h" +#include "device.h" +#include "initvol.h" +#include "ods2.h" +#include "phyvirt.h" +#include "stsdef.h" +#include "compat.h" +#include "sysmsg.h" +#include "vmstime.h" + +extern vmscond_t mountdef( const char *devnam ); + +struct VCB *vcb_list = NULL; + +/* WCBKEY passes info to compare/create routines */ + +struct WCBKEY { + unsigned vbn; + struct FCB *fcb; + struct WCB *prevwcb; +}; + +static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *scb ); + +#define DEBUGx + +/***************************************************************** checksum() */ + +/* checksum() to produce header checksum values... */ + +f11word checksumn( f11word *block, int count ) { + register unsigned result; + register unsigned short *ptr; + register unsigned data; + + ptr = block; + result = 0; + for ( ; count > 0; --count ) { + data = *ptr++; + result += F11WORD(data); + } + return (f11word)(result & 0xFFFF); +} + +f11word checksum( f11word *block ) { + return checksumn( block, 255 ); +} + +/***************************************************************** fid_copy() */ + +/* fid_copy() copy fid from file header with default rvn! */ + +void fid_copy( struct fiddef *dst, struct fiddef *src, unsigned rvn ) { + 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; +} + +/************************************************************* deaccesshead() */ + +/* deaccesshead() release header from INDEXF... */ + +vmscond_t deaccesshead( struct VIOC *vioc, struct HEAD *head, uint32_t idxblk ) { + f11word check; + + if ( head && idxblk ) { + check = checksum( (f11word *) head ); + head->fh2$w_checksum = F11WORD(check); + } + return deaccesschunk( vioc, idxblk, 1, TRUE ); +} + +/*************************************************************** accesshead() */ + +/* accesshead() find file or extension header from INDEXF... */ + +vmscond_t accesshead( struct VCB *vcb, struct fiddef *fid, unsigned seg_num, + struct VIOC **vioc, struct HEAD **headbuff, + uint32_t *retidxblk, unsigned wrtflg ) { + register vmscond_t sts; + register struct VCBDEV *vcbdev; + register uint32_t idxblk; + + vcbdev = RVN_TO_DEV(vcb, fid->fid$b_rvn); + if( vcbdev == NULL ) + return SS$_DEVNOTMOUNT; + + if( wrtflg && !(vcb->status & VCB_WRITE) ) + return SS$_WRITLCK; + + idxblk = fid->fid$w_num + + (fid->fid$b_nmx << 16) - 1 + + F11WORD(vcbdev->home.hm2$w_ibmapvbn) + + F11WORD(vcbdev->home.hm2$w_ibmapsize); + + if (vcbdev->idxfcb->head != NULL) { + if (idxblk >= + 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; + } + } + + if( $SUCCESSFUL(sts = accesschunk( vcbdev->idxfcb, idxblk, vioc, + (char **) headbuff, NULL, + wrtflg ? 1 : 0 )) ) { + register struct HEAD *head = *headbuff; + if( retidxblk ) { + *retidxblk = wrtflg ? idxblk : 0; + } + if (F11WORD(head->fh2$w_fid.fid$w_num) != fid->fid$w_num || + head->fh2$w_fid.fid$b_nmx != fid->fid$b_nmx || + 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)) { + sts = SS$_NOSUCHFILE; + } else if( head->fh2$b_idoffset < 38 || + head->fh2$b_idoffset > head->fh2$b_mpoffset || + head->fh2$b_mpoffset > head->fh2$b_acoffset || + head->fh2$b_acoffset > head->fh2$b_rsoffset || + head->fh2$b_map_inuse > + head->fh2$b_acoffset - head->fh2$b_mpoffset || + checksum( (f11word *) head ) != + F11WORD( head->fh2$w_checksum ) ) { +#if DEBUG + printf( "--->accesshead(): File header checksum failure:" ); + printf( " FH2$W_CHECKSUM=%u, checksum()=%u\n", + F11WORD( head->fh2$w_checksum ), + checksum( (f11word *) head ) + ); +#endif + sts = $SETLEVEL(SS$_BADCHKSUM, SEVERE); + } else if( F11WORD(head->fh2$w_seg_num) != seg_num ) { + sts = SS$_FILESEQCHK; + } + + if( $FAILED(sts) ) { + deaccesschunk( *vioc, 0, 0, FALSE ); + } + } + return sts; +} + +/************************************************************** wcb_compare() */ + +/* wcb_compare() compare two windows - return -1 for less, 0 for match */ + +/* as a by product keep highest previous entry so that if a new window + * is required we don't have to go right back to the initial file header + */ + +static int wcb_compare( uint32_t hashval, void *keyval, void *thiswcb ) { + + register struct WCBKEY *wcbkey; + register struct WCB *wcb; + + UNUSED(hashval); + + wcbkey = (struct WCBKEY *) keyval; + wcb = (struct WCB *) thiswcb; + + if( wcbkey->vbn < wcb->loblk ) { + return -1; /* Search key is less than this window maps */ + } + + if (wcbkey->vbn <= wcb->hiblk) { + return 0; /* Search key must be in this window */ + } + if (wcbkey->prevwcb == NULL) { + wcbkey->prevwcb = wcb; + } else { + if (wcb->loblk != 0 && wcb->hiblk > wcbkey->prevwcb->hiblk) { + wcbkey->prevwcb = wcb; + } + } + return 1; /* Search key is higher than this window... */ +} + +/************************************************************ premap_indexf() */ + +/* premap_indexf() called to physically read the header for indexf.sys + * so that indexf.sys can be mapped and read into virtual cache.. + */ + +struct HEAD *premap_indexf( struct FCB *fcb, vmscond_t *retsts ) { + struct HEAD *head; + struct VCBDEV *vcbdev; + int iderr; + + vcbdev = RVN_TO_DEV( fcb->vcb, fcb->rvn ); + if( vcbdev == NULL ) { + *retsts = SS$_DEVNOTMOUNT; + return NULL; + } + head = (struct HEAD *) malloc(sizeof(struct HEAD)); + if( head == NULL ) { + *retsts = SS$_INSFMEM; + return NULL; + } + + if( $FAILS(*retsts = virt_read( vcbdev->dev, + F11LONG( vcbdev->home.hm2$l_ibmaplbn ) + + F11WORD( vcbdev->home.hm2$w_ibmapsize ), + sizeof( struct HEAD ), (char *) head )) ) { + free( head ); + return NULL; + } + if( (iderr = (F11WORD(head->fh2$w_fid.fid$w_num) != FID$C_INDEXF || + head->fh2$w_fid.fid$b_nmx != 0 || + F11WORD(head->fh2$w_fid.fid$w_seq) != FID$C_INDEXF)) || + F11WORD(head->fh2$w_checksum) != checksum( (f11word *) head ) ) { +#if DEBUG + printf( "--->premap_indexf(): Index file header %s" ); + printf( " failure: FH2$W_CHECKSUM=%u, checksum()=%u\n", + (iderr? "File ID match" : "checksum"), + F11WORD( head->fh2$w_checksum ), + checksum( (f11word *) head ) + ); +#endif + *retsts = $SETLEVEL((iderr? SS$_FILENUMCHK: SS$_BADCHKSUM), SEVERE); + free( head ); + return NULL; + } + + return head; +} + +/*************************************************************** wcb_create() */ + +/* wcb_create() creates a window control block by reading appropriate + * file headers... + */ + +static void *wcb_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { + register struct WCB *wcb; + uint32_t curvbn; + unsigned extents = 0; + struct HEAD *head; + struct VIOC *vioc = NULL; + register struct WCBKEY *wcbkey; + + UNUSED(hashval); + + wcb = (struct WCB *) calloc( sizeof(struct WCB), 1 ); + + if( wcb == NULL ) { + *retsts = SS$_INSFMEM; + return NULL; + } + wcbkey = (struct WCBKEY *) keyval; + wcb->cache.objmanager = NULL; + wcb->cache.objtype = OBJTYPE_WCB; + + if( wcbkey->prevwcb == NULL ) { + curvbn = + wcb->loblk = 1; + wcb->hd_seg_num = 0; + head = wcbkey->fcb->head; + if( head == NULL ) { + head = premap_indexf( wcbkey->fcb, retsts ); + if (head == NULL) { + free( wcb ); + return NULL; + } + head->fh2$w_ext_fid.fid$w_num = 0; + head->fh2$w_ext_fid.fid$b_nmx = 0; + } + fid_copy(&wcb->hd_fid, &head->fh2$w_fid, wcbkey->fcb->rvn); + } else { + wcb->loblk = wcbkey->prevwcb->hiblk + 1; + curvbn = wcbkey->prevwcb->hd_basevbn; + wcb->hd_seg_num = wcbkey->prevwcb->hd_seg_num; + memcpy( &wcb->hd_fid, &wcbkey->prevwcb->hd_fid, sizeof(struct fiddef) ); + head = wcbkey->fcb->head; + } + while( TRUE ) { + register f11word *mp; + register f11word *me; + + wcb->hd_basevbn = curvbn; + + if( wcb->hd_seg_num != 0 ) { + if( $FAILS(*retsts = accesshead(wcbkey->fcb->vcb, &wcb->hd_fid, + wcb->hd_seg_num, &vioc, &head, NULL, 0)) ) { + free(wcb); + return NULL; + } + } + mp = (f11word *) head + head->fh2$b_mpoffset; + me = mp + head->fh2$b_map_inuse; + while( mp < me ) { + register uint32_t phylen, phyblk; + register f11word mp0 = 0; + + switch( ((mp0 = F11WORD(*mp)) >> 14) & 0x3 ) { + case 0: + phylen = 0; + mp++; + break; + case 1: + phylen = (mp0 & 0x00ff) + 1; + phyblk = ((mp0 & 0x3f00) << 8) | F11WORD(mp[1]); + mp += 2; + break; + case 2: + phylen = (mp0 & 0x3fff) + 1; + phyblk = (F11WORD(mp[2]) << 16) | F11WORD(mp[1]); + mp += 3; + break; + case 3: default: + phylen = ((mp0 & 0x3fff) << 16) + F11WORD(mp[1]) + 1; + phyblk = (F11WORD(mp[3]) << 16) | F11WORD(mp[2]); + mp += 4; + break; + } + curvbn += phylen; + if( phylen != 0 && curvbn > wcb->loblk ) { + wcb->phylen[extents] = phylen; + wcb->phyblk[extents] = phyblk; + wcb->rvn[extents] = wcb->hd_fid.fid$b_rvn; + if( ++extents >= EXTMAX) { + if( curvbn > wcbkey->vbn ) { + break; + } else { + extents = 0; + wcb->loblk = curvbn; + } + } + } + } + if( extents >= EXTMAX || + (F11WORD(head->fh2$w_ext_fid.fid$w_num) == 0 && + head->fh2$w_ext_fid.fid$b_nmx == 0) ) { + break; + } + { + register unsigned rvn; + wcb->hd_seg_num++; + rvn = wcb->hd_fid.fid$b_rvn; + fid_copy(&wcb->hd_fid, &head->fh2$w_ext_fid, rvn); + if( vioc != NULL ) { + deaccesshead(vioc, NULL, 0); + vioc = NULL; + } + } + } + if( vioc != NULL ) { + deaccesshead(vioc, NULL, 0); + vioc = NULL; + } else { + if( wcbkey->fcb->head == NULL ) + free(head); + } + wcb->hiblk = curvbn - 1; + wcb->extcount = extents; + *retsts = SS$_NORMAL; + if( curvbn <= wcbkey->vbn ) { + free(wcb); + wcb = NULL; +#if DEBUG + printf( "--->wcb_create(): curvbn (=%u) <= wcbkey->vbn (=%u)\n", + curvbn, wcbkey->vbn ); +#endif + *retsts = SS$_DATACHECK; + } + + return wcb; +} + +/**************************************************************** getwindow() */ + +/* getwindow() find a window to map VBN to LBN ... */ + +vmscond_t getwindow( struct FCB * fcb, uint32_t vbn, struct VCBDEV **devptr, + uint32_t *phyblk, uint32_t *phylen, struct fiddef *hdrfid, + unsigned *hdrseq ) { + vmscond_t sts; + struct WCB *wcb; + struct WCBKEY wcbkey; + register unsigned extent = 0; + register unsigned togo; + +#if DEBUG + printf("Accessing window for vbn %u, file (%u)\n", vbn, fcb->cache.hashval); +#endif + memset( &wcbkey, 0, sizeof( wcbkey ) ); + wcbkey.vbn = vbn; + wcbkey.fcb = fcb; + wcbkey.prevwcb = NULL; + wcb = cache_find( (void *) &fcb->wcb, 0, &wcbkey, &sts, wcb_compare, wcb_create ); + if( wcb == NULL ) + return sts; + + togo = vbn - wcb->loblk; + while( togo >= wcb->phylen[extent] ) { + togo -= wcb->phylen[extent]; + if( ++extent > wcb->extcount ) + return SS$_BUGCHECK; + } + *devptr = RVN_TO_DEV(fcb->vcb, wcb->rvn[extent]); + *phyblk = wcb->phyblk[extent] + togo; + *phylen = wcb->phylen[extent] - togo; + + if( hdrfid != NULL ) + memcpy(hdrfid, &wcb->hd_fid, sizeof(struct fiddef)); + + if( hdrseq != NULL ) + *hdrseq = wcb->hd_seg_num; +#if DEBUG + printf("Mapping vbn %u to %u (%u -> %u)[%u] file (%u)\n", + vbn, *phyblk, wcb->loblk, wcb->hiblk, wcb->hd_basevbn, + fcb->cache.hashval); +#endif + cache_untouch( &wcb->cache, TRUE ); + + if( *devptr == NULL ) + return SS$_DEVNOTMOUNT; + + return SS$_NORMAL; +} + +/************************************************************* vioc_manager() */ + +/* Object manager for VIOC objects:- if the object has been + * modified then we need to flush it to disk before we let + * the cache routines do anything to it... + */ + +void *vioc_manager( struct CACHE * cacheobj, int flushonly ) { + register struct VIOC *vioc; + + UNUSED(flushonly); + + vioc = (struct VIOC *) cacheobj; + + if( vioc->modmask != 0 ) { + register struct FCB *fcb = NULL; + register unsigned int length = VIOC_CHUNKSIZE; + register uint32_t curvbn = 0; + register char *address = NULL; + register unsigned modmask = 0; + + fcb = vioc->fcb; + curvbn = (size_t)vioc->cache.hashval + 1; + address = (char *) vioc->data; + modmask = vioc->modmask; + +#if DEBUG + printf("\nvioc_manager writing vbn %d\n", curvbn); +#endif + do { + register vmscond_t sts; + unsigned int wrtlen = 0; + uint32_t phyblk = 0, phylen = 0, idxlbn; + struct VCBDEV *vcbdev = NULL; + + while( length > 0 && !(1 & modmask) ) { + length--; + curvbn++; + address += 512; + modmask = modmask >> 1; + } + while (wrtlen < length && (1 & modmask) ) { + wrtlen++; + modmask = modmask >> 1; + } + length -= wrtlen; + while( wrtlen > 0 ) { + if( fcb->highwater != 0 && curvbn >= fcb->highwater ) { + length = 0; + break; + } + if( $FAILS(sts = getwindow(fcb, curvbn, &vcbdev, + &phyblk, &phylen, NULL, NULL)) ) + return NULL; + if (phylen > wrtlen) + phylen = wrtlen; + if (fcb->highwater != 0 && + curvbn + phylen > fcb->highwater) { + phylen = fcb->highwater - curvbn; + } + if( $FAILS(sts = virt_write( vcbdev->dev, phyblk, + phylen * 512, address )) ) + return NULL; + + idxlbn = (F11LONG(vcbdev->home.hm2$l_ibmaplbn) + + F11WORD(vcbdev->home.hm2$w_ibmapsize)); + + if( idxlbn >= phyblk && idxlbn < phyblk + phylen ) { + if( $FAILS(sts = virt_write( vcbdev->dev, + F11LONG(vcbdev->home.hm2$l_altidxlbn), + 512, address + + (512 * ((size_t)idxlbn - phyblk)) )) ) + return NULL; + } + wrtlen -= phylen; + curvbn += phylen; + address += (size_t)phylen * 512; + } + } while( length > 0 && modmask != 0 ); + vioc->modmask = 0; + vioc->cache.objmanager = NULL; + } + return cacheobj; +} + +/************************************************************ deaccesschunk() */ + +/* deaccesschunk() to deaccess a VIOC (chunk of a file) */ + +vmscond_t deaccesschunk( struct VIOC *vioc, uint32_t wrtvbn, + int wrtblks, int reuse ) { +#if DEBUG + printf( "Deaccess chunk %u (%08x) wrtvbn: %u blks: %u ru: %u\n", + vioc->cache.hashval, vioc->cache.hashval, wrtvbn, wrtblks, reuse ); +#endif + + if( wrtvbn ) { + register unsigned modmask; + + if( wrtvbn <= vioc->cache.hashval || + wrtvbn + wrtblks > vioc->cache.hashval + VIOC_CHUNKSIZE + 1 ) { +#if DEBUG + printf( "VBN out of reserved range\n" ); +#endif + return SS$_BADPARAM; + } + + modmask = 1 << (wrtvbn - vioc->cache.hashval - 1); + + while( --wrtblks > 0 ) + modmask |= modmask << 1; + + if( (vioc->wrtmask | modmask) != vioc->wrtmask ) { +#if DEBUG + printf( "Write mask error %08x %08x\n", + (vioc->wrtmask | modmask), vioc->wrtmask ); +#endif + return SS$_WRITLCK; + } + + vioc->modmask |= modmask; + if( vioc->cache.refcount == 1 ) + vioc->wrtmask = 0; + vioc->cache.objmanager = vioc_manager; + } + + cache_untouch( &vioc->cache, reuse ); + return SS$_NORMAL; +} + +/************************************************************** vioc_create() */ + +static void *vioc_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { + + register struct VIOC *vioc; + register uint32_t length; + register uint32_t curvbn; + register char *address; + register struct FCB *fcb; + + vioc = (struct VIOC *) calloc( 1, sizeof(struct VIOC) ); + if( vioc == NULL ) { + *retsts = SS$_INSFMEM; + return NULL; + } + + curvbn = hashval + 1; + fcb = (struct FCB *) keyval; + + vioc->cache.objmanager = NULL; + vioc->cache.objtype = OBJTYPE_VIOC; + vioc->fcb = fcb; + vioc->wrtmask = 0; + vioc->modmask = 0; + length = fcb->hiblock - curvbn + 1; + if( length > VIOC_CHUNKSIZE ) + length = VIOC_CHUNKSIZE; + address = (char *) vioc->data; + do { + register vmscond_t sts; + uint32_t phyblk = 0, phylen = 0; + struct VCBDEV *vcbdev = NULL; + + if (fcb->highwater != 0 && curvbn >= fcb->highwater) { + memset( address, 0, (size_t)length * 512 ); + break; + } + + if( $SUCCESSFUL(sts = getwindow( fcb, curvbn, &vcbdev, + &phyblk, &phylen, NULL, NULL )) ) { + if( phylen > length ) + phylen = length; + if( fcb->highwater != 0 && curvbn + phylen > fcb->highwater ) { + phylen = fcb->highwater - curvbn; + } + sts = virt_read( vcbdev->dev, phyblk, phylen * 512, + address ); + } + if( $FAILED(sts) ) { + free(vioc); + *retsts = sts; + return NULL; + } + length -= phylen; + curvbn += phylen; + address += (size_t)phylen * 512; + } while( length > 0 ); + *retsts = SS$_NORMAL; + + return vioc; +} + +/************************************************************** accesschunk() */ + +/* accesschunk() return pointer to a 'chunk' of a file ... */ + +vmscond_t accesschunk( struct FCB *fcb, uint32_t vbn, struct VIOC **retvioc, + char **retbuff, uint32_t *retblocks, unsigned wrtblks ) { + vmscond_t sts; + register uint32_t blocks; + register struct VIOC *vioc; + +#if DEBUG + printf("Access chunk %u (%u)\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 (vbn + wrtblks > blocks + VIOC_CHUNKSIZE + 1) { + return SS$_BADPARAM; + } + } + vioc = cache_find((void *) &fcb->vioc, blocks, fcb, &sts, NULL, vioc_create); + if (vioc == NULL) + return sts; + /* + Return result to caller... + */ + *retvioc = vioc; + blocks = vbn - blocks - 1; + *retbuff = vioc->data[blocks]; + if (wrtblks || retblocks != NULL) { + register unsigned modmask = 1 << blocks; + + blocks = VIOC_CHUNKSIZE - blocks; + if (vbn + blocks > fcb->hiblock) + blocks = fcb->hiblock - vbn + 1; + if (wrtblks && blocks > wrtblks) + blocks = wrtblks; + if (retblocks != NULL) + *retblocks = blocks; + if (wrtblks && blocks) { + if( fcb->highwater != 0 && vbn + blocks > fcb->highwater ) { + fcb->highwater = vbn + blocks; + fcb->head->fh2$l_highwater = F11LONG( fcb->highwater ); + } + while( --blocks > 0 ) + modmask |= modmask << 1; + vioc->wrtmask |= modmask; + vioc->cache.objmanager = vioc_manager; + } + } + return SS$_NORMAL; +} + +/************************************************************* deaccessfile() */ + +/* deaccessfile() finish accessing a file.... */ + +vmscond_t deaccessfile( struct FCB *fcb ) { +#if DEBUG + printf("Deaccessing file (%u) reference %d\n", fcb->cache.hashval, + fcb->cache.refcount); +#endif + if( fcb->cache.refcount == 1 ) { + register unsigned refcount; + + refcount = cache_refcount((struct CACHE *) fcb->vioc); + if( refcount && fcb->vioc == fcb->headvioc ) { + /* Index file header and data may share a VIOC. + * In this case, there's no way to know how + * many of the refs are from each source. Assume 1 from header. + */ + --refcount; + } + refcount += cache_refcount((struct CACHE *) fcb->wcb); + + if( refcount != 0 ) { +#if DEBUG + printf("File reference counts non-zero %d (%d)\n", refcount, + fcb->cache.hashval); +#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 ) && + (fcb->head->fh2$l_filechar & F11LONG(FH2$M_MARKDEL)) ) { + return deallocfile(fcb); + } + } + cache_untouch(&fcb->cache, TRUE); + return SS$_NORMAL; +} + +/************************************************************** fcb_manager() */ + +/* Object manager for FCB objects:- we point to one of our + sub-objects (vioc or wcb) in preference to letting the + cache routines get us! But we when run out of excuses + it is time to clean up the file header... :-( */ + +void *fcb_manager(struct CACHE *cacheobj, int flushonly) { + register struct FCB *fcb; + + fcb = (struct FCB *) cacheobj; + if( fcb->vioc != NULL ) + return &fcb->vioc->cache; + if( fcb->wcb != NULL ) + return &fcb->wcb->cache; + if( fcb->cache.refcount != 0 || flushonly) + return NULL; + if( fcb->headvioc != NULL ) { + deaccesshead(fcb->headvioc, fcb->head, fcb->headvbn); + fcb->headvioc = NULL; + } + return cacheobj; +} + +/*************************************************************** fcb_create() */ + +static void *fcb_create( uint32_t filenum, void *keyval, vmscond_t *retsts ) { + + register struct FCB *fcb; + + fcb = (struct FCB *) calloc( 1, sizeof(struct FCB) ); + + UNUSED(filenum); + UNUSED(keyval); + + if (fcb == NULL) { + *retsts = SS$_INSFMEM; + } else { + fcb->cache.objmanager = fcb_manager; + fcb->cache.objtype = OBJTYPE_FCB; + fcb->hiblock = 100000; + } + return fcb; +} + +/*************************************************************** accessfile() */ + +/* accessfile() open up file for access... */ + +vmscond_t accessfile( struct VCB * vcb, struct fiddef * fid, struct FCB **fcbadd, + unsigned wrtflg ) { + vmscond_t sts; + register struct FCB *fcb; + register uint32_t filenum; + + filenum = (fid->fid$b_nmx << 16) + fid->fid$w_num; +#if DEBUG + printf("Accessing file (%d,%d,%d) wrt %u\n", (fid->fid$b_nmx << 16) + + fid->fid$w_num, fid->fid$w_seq, fid->fid$b_rvn, wrtflg); +#endif + if( filenum < 1 ) + return SS$_BADPARAM; + 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; + /* If not found make one... */ + *fcbadd = fcb; + if( fcb->vcb == NULL ) { + fcb->rvn = fid->fid$b_rvn; + if( fcb->rvn == 0 && vcb->devices > 1 ) + fcb->rvn = 1; + fcb->vcb = vcb; + } + if( wrtflg ) { + if( fcb->headvioc != NULL && !(fcb->status & FCB_WRITE) ) { + deaccesshead(fcb->headvioc, NULL, 0); + fcb->headvioc = NULL; + } + fcb->status |= FCB_WRITE; + } + if( fcb->headvioc == NULL ) { + register vmscond_t sts; + + if( $SUCCESSFUL(sts = accesshead(vcb, fid, 0, &fcb->headvioc, + &fcb->head, &fcb->headvbn, + wrtflg)) ) { + fcb->hiblock = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); + if (fcb->head->fh2$b_idoffset > 39) { + fcb->highwater = F11LONG(fcb->head->fh2$l_highwater); + } else { + fcb->highwater = 0; + } + } else { +#if DEBUG + printf("Accessfile fail status %08x\n", sts); +#endif + fcb->cache.objmanager = NULL; + cache_untouch(&fcb->cache, TRUE); + cache_delete(&fcb->cache); + return sts; + } + } + return SS$_NORMAL; +} + +/***************************************************************** dismount() */ + +/* dismount() finish processing on a volume */ + +vmscond_t dismount (struct VCB * vcb, options_t options ) { + register vmscond_t sts, device; + int expectfiles; + int openfiles; + struct VCBDEV *vcbdev; + + expectfiles = vcb->devices; + openfiles = cache_refcount( &vcb->fcb->cache ); + + if( vcb->status & VCB_WRITE ) + expectfiles *= 2; +#if DEBUG + printf("Dismounting disk %d\n", openfiles); +#endif + sts = SS$_NORMAL; + if( openfiles != expectfiles ) { + printmsg( DISM_CANNOTDMT, 0, + 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? + vcb->vcbdev[0].home.hm2$t_strucname: + vcb->vcbdev[0].home.hm2$t_volname) ) ); + return printmsg( DISM_USERFILES, MSG_CONTINUE, + DISM_CANNOTDMT, openfiles - expectfiles ); + } + + vcbdev = vcb->vcbdev; + for( device = 0; device < vcb->devices; device++, vcbdev++ ) { +#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 ); +#if DEBUG + printf( "--->dismount(): %s = deaccessfile(bitmap)\n", getmsg(sts, 0) ); +#endif + if( $FAILED(sts) ) { + printmsg( DISM_CANNOTDMT, 0, 12, vcbdev->home.hm2$t_volname ); + sts = printmsg( sts, MSG_CONTINUE, DISM_CANNOTDMT ); + break; + } + vcbdev->mapfcb->status &= ~FCB_WRITE; + vcbdev->mapfcb = NULL; + } + + while (vcb->dircache) { + cache_delete((struct CACHE *) vcb->dircache); +#if DEBUG + printf( "--->dismount(): cache_delete(dircache)\n" ); +#endif + } + /* Remove all unreferenced fcbs - the LRU list still has references + * on file headers. This reduces the references to just the index file itself. + */ + cache_remove( &vcb->fcb->cache ); +#if DEBUG + printf( "--->dismount(): cache_remove()\n" ); +#endif + + /* Index file data & header. Special handling due to header also being + * file data for the index file. + */ + deaccesshead( vcbdev->idxfcb->headvioc, + vcbdev->idxfcb->head, vcbdev->idxfcb->headvbn ); + /* Check status? */ + vcbdev->idxfcb->headvioc = NULL; + cache_untouch(&vcbdev->idxfcb->cache, FALSE); + vcbdev->idxfcb->status &= ~FCB_WRITE; + vcbdev->idxfcb = NULL; + /* Remove index fcb, which should be the last in the FCB cache. */ + + cache_remove( &vcb->fcb->cache ); +#if DEBUG + printf( "--->dismount(): %s = deaccess(index)\n", getmsg(sts, 0) ); +#endif + if( $FAILED(sts) ) + break; + } + } + if( $SUCCESSFUL(sts) ) { + struct VCB *lp; + + vcbdev = vcb->vcbdev; + for (device = 0; device < vcb->devices; device++, vcbdev++) { + if( vcbdev->dev != NULL ) { +#if DEBUG + printf( "--->dismount(): virt_close(%s)\n", + vcbdev->dev->devnam ); +#endif + virt_close( vcbdev->dev ); + vcbdev->dev = NULL; + if( options & MOU_LOG ) + sts = printmsg( (vcb->devices > 1)? + DISM_DISMOUNTD: DISM_DISMOUNTDV, + 0, + 12, vcbdev->home.hm2$t_volname, + 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? + vcb->vcbdev[0].home.hm2$t_strucname: + vcb->vcbdev[0].home.hm2$t_volname) ) ); + } + } + for( lp = (struct VCB *)&vcb_list; lp->next != NULL; lp = lp->next ) { + if( lp->next == vcb ) { + lp->next = vcb->next; + break; + } + } + if( vcb->devices > 1 ) + sts = printmsg( DISM_DISMAL, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); + free(vcb); + } + + return sts; +} + +/******************************************************************** mount() */ + +#if DEBUG +#ifndef HOME_SKIP +#define HOME_SKIP 1 +#endif +#ifndef HOME_LIMIT +#define HOME_LIMIT 3 +#endif +#else +#ifndef HOME_SKIP +#define HOME_SKIP 0 +#endif +#ifndef HOME_LIMIT +#define HOME_LIMIT 1000 +#endif +#endif + +/* mount() make disk volume available for processing... */ + +vmscond_t mount( options_t flags, + unsigned devices, + char *devnam[], + char *label[] ) { + register unsigned device; + vmscond_t sts = 0; + struct VCB *vcb; + struct VCBDEV *vcbdev; + struct VOLSETREC *volsets = NULL; + +#if DEBUG + if (sizeof(struct HOME) != 512 || sizeof(struct HEAD) != 512) + return SS$_BUGCHECK; +#endif + + /* calloc() initializes most of the structure */ + + vcb = (struct VCB *) calloc( 1, sizeof(struct VCB) + + (((size_t)devices - 1) * sizeof(struct VCBDEV)) ); + if( vcb == NULL ) + return SS$_INSFMEM; + + if( flags & MOU_WRITE ) + vcb->status |= VCB_WRITE; + + /* Pass 1: validate device name & open each for I/O. + * Read HOM blocks & verify labels. + */ + vcbdev = vcb->vcbdev; + for( device = 0; device < devices; device++, vcbdev++ ) { + char *dname; + uint32_t errcnt = 0; + + dname = devnam[device]; + sts = MOUNT_FAILED; + vcbdev->dev = NULL; + if( *dname != '\0' ) { /* Really want to allow skipping volumes? */ + unsigned int hba, delta, homtry; + + if( label[device] != NULL && strlen(label[device]) > + sizeof( volsets[0].vsr$t_label ) ) { + sts = printmsg( MOUNT_BADLABEL, 0, label[device] ); + break; + } + if( $FAILS(sts = virt_open( &dname, flags, &vcbdev->dev )) ) + break; + vcb->devices++; + if (vcbdev->dev->vcb != NULL) { + sts = printmsg( SS$_DEVMOUNT, 0 ); + break; + } + if( (flags & MOU_WRITE ) && !(vcbdev->dev->access & MOU_WRITE) ) { + sts = printmsg( MOUNT_WRITELCK, 0, devnam[device] ); + vcb->status &= ~VCB_WRITE; + } + + delta = delta_from_index( (vcbdev->dev->access & MOU_DEVTYPE) >> MOU_V_DEVTYPE ); + + for( hba = 1, homtry = 0; + homtry < HOME_LIMIT; homtry++, hba += delta ) { + struct HOME *hom; + +#if HOME_SKIP > 100 + if( homtry < HOME_SKIP ) + continue; +#endif + if( $FAILS(sts = virt_read( vcbdev->dev, hba, + sizeof( struct HOME ), + (char *) &vcbdev->home )) ) + break; + hom = &vcbdev->home; +#if DEBUG || defined( HOME_LOG ) + printf( "--->mount(%u): LBA=%u, HM2$L_HOMELBN=%u, " + "HM2$L_ALHOMELBN=%u, " + "HM2$T_FORMAT=\"%12.12s\", memcmp()=%d,%d\n", + homtry+1, hba, F11LONG( hom->hm2$l_homelbn ), + F11LONG( hom->hm2$l_alhomelbn ), + hom->hm2$t_format, + memcmp( hom->hm2$t_format, "DECFILE11B ", 12 ), + memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) + ); +#endif + if( (hba == F11LONG(hom->hm2$l_homelbn)) && + ((F11LONG(hom->hm2$l_alhomelbn) != 0) || + (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 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) )) && + (F11WORD(hom->hm2$w_checksum2) == + checksum( (f11word *)hom )) && + ((memcmp(hom->hm2$t_format, "DECFILE11B ", 12) == 0) || + (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 0)) ) { + break; + } + errcnt++; + sts = MOUNT_HOMBLKCHK; +#if DEBUG || defined( HOME_LOG ) + printf( "--->mount(): Home block validation failure\n" ); + 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( "(F11WORD(hom->hm2$w_checksum2) = %u %u\n", F11WORD(hom->hm2$w_checksum2), + checksum( (f11word *)hom )); +#endif + } + if( $FAILED(sts) ) { + sts = printmsg( sts, 0, errcnt ); + break; + } + if( (F11WORD(vcbdev->home.hm2$w_struclev) >> 8) != 2 || + (F11WORD(vcbdev->home.hm2$w_struclev) & 0xFFu) < 1 ) { + sts = printmsg( sts, 0, + F11WORD(vcbdev->home.hm2$w_struclev) >> 8, + F11WORD(vcbdev->home.hm2$w_struclev) & 0XFFu ); + break; + } + if( errcnt && (flags & MOU_LOG) ) /* Suppress warning if /nolog for scripted mounts */ + printmsg( $SETLEVEL(MOUNT_HOMBLKCHK, WARNING), 0, errcnt ); + + if( label[device] != NULL ) { + int i; + char lbl[12+1]; /* Pad CLI-supplied label to match ODS */ + + (void) snprintf( lbl, sizeof(lbl), "%-12s", label[device] ); + + for( i = 0; i < 12; i++ ) { + if( toupper( lbl[i] ) != + toupper( vcbdev->home.hm2$t_volname[i] ) ) { + sts = printmsg( MOUNT_WRONGVOL, 0, dname, + 12, vcbdev->home.hm2$t_volname, lbl ); + break; + } + } + if( $FAILED(sts) ) + break; + } + if( (F11WORD(vcbdev->home.hm2$w_rvn) != device + 1) && + !(F11WORD(vcbdev->home.hm2$w_rvn) == 0 && device == 0) ) { + if( vcbdev->home.hm2$w_rvn == 0 ) { + sts = printmsg( MOUNT_NOTVSMEM, 0, dname, + 12, vcbdev->home.hm2$t_volname, + device+1, + 12, vcb->vcbdev[0].home.hm2$t_strucname ); + } else { + sts = printmsg( MOUNT_WRONGRVN, 0, dname, + 12, vcbdev->home.hm2$t_volname, + F11WORD(vcbdev->home.hm2$w_rvn), + 12, vcbdev->home.hm2$t_strucname, + device + 1, + 12, vcb->vcbdev[0].home.hm2$t_strucname ); + } + } + if( $FAILED(sts) ) + break; + } /* for(each device) */ + } + if( $FAILED(sts) ) { + vcbdev = vcb->vcbdev; + for( device = 0; device < vcb->devices; device++, vcbdev++ ) { + if (vcbdev->dev == NULL) { + continue; + } + virt_close( vcbdev->dev ); + } + free( vcb ); + vcb = NULL; + } else { + /* Pass 2: Open the index file. + * For RVN 1 of a volume set, read VOLSET.SYS. + * For writable volumes, open and validate BITMAP.SYS. + */ + vcbdev = vcb->vcbdev; + for( device = 0; device < devices; device++, vcbdev++ ) { + char *dname; + + dname = devnam[device]; + sts = MOUNT_FAILED; + vcbdev->idxfcb = NULL; + vcbdev->mapfcb = NULL; + vcbdev->clustersize = 0; + vcbdev->max_cluster = 0; + vcbdev->free_clusters = 0; + + if( *dname != '\0' ) { + struct fiddef idxfid = { FID$C_INDEXF, FID$C_INDEXF, 0, 0 }; + idxfid.fid$b_rvn = device + 1; + + if( !(vcb->status & ~VCB_WRITE) ) + vcbdev->dev->access &= ~MOU_WRITE; + + if( $FAILS(sts = accessfile( vcb, &idxfid, &vcbdev->idxfcb, + (vcb->status & VCB_WRITE) != 0 )) ) { + virt_close( vcbdev->dev ); + vcbdev->dev = NULL; + break; + } + vcbdev->dev->vcb = vcb; + if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) { + if( device == 0 ) { + struct fiddef vsfid = { FID$C_VOLSET, + FID$C_VOLSET, 1, 0 } ; + struct FCB *vsfcb = NULL; + struct VIOC *vioc = NULL; + unsigned rec; + uint32_t vbn = 1; + struct VOLSETREC *bufp = NULL; + + unsigned setcount; + + setcount = F11WORD(vcbdev->home.hm2$w_setcount); + if( setcount != devices ) { + sts = printmsg( MOUNT_WRONGVOLCNT, 0, + 12, vcbdev->home.hm2$t_strucname, + setcount, devices ); + break; + } + /* Read VOLSET.SYS + * 1 record for volset name + 1 for each member's label. + */ + volsets = + (struct VOLSETREC *)malloc( (1+(size_t)setcount) * + sizeof( struct VOLSETREC ) ); + if( volsets == NULL ) { + printmsg( MOUNT_VOLSETSYS, 0, dname ); + sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, MOUNT_VOLSETSYS ); + break; + } + if( $FAILS(sts = accessfile( vcb, &vsfid, &vsfcb, 0 )) ) { + printmsg( MOUNT_VOLSETSYS, 0, dname ); + sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); + break; + } + for( rec = 0; rec <= setcount; ) { + uint32_t blocks = 0, recs; + + if( $FAILS(sts = accesschunk( vsfcb, vbn, &vioc, + (char **)&bufp, + &blocks, 0 )) ) + break; + vbn += blocks; + + recs = (size_t)blocks * 512 / sizeof( struct VOLSETREC ); + if( rec + recs > setcount + 1 ) + recs = setcount + 1 - rec; + + memcpy(volsets+rec, bufp, recs * + sizeof( struct VOLSETREC )); + rec += recs; + + deaccesschunk( vioc, 0, 0, 0 ); + } + + { vmscond_t st2; + st2 = deaccessfile( vsfcb ); + if( $SUCCESSFUL(sts) ) + sts = st2; + } + if( $FAILED(sts) ) { + printmsg( MOUNT_VOLSETSYS, 0, dname ); + sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); + break; + } + if( memcmp(vcbdev->home.hm2$t_strucname, + volsets[0].vsr$t_label, 12 ) != 0 ) { + + sts = printmsg( MOUNT_WRONGVOLMEM, 0, + dname, + 12, vcbdev->home.hm2$t_strucname, + 12, volsets[0].vsr$t_label ); + break; + } + } else { /* device != 0 */ + if( vcb->vcbdev[0].dev == NULL ) { + sts = printmsg( MOUNT_RVN1NOTMNT, 0 ); + break; + } + if( memcmp(vcbdev->home.hm2$t_strucname, + vcb->vcbdev[0].home.hm2$t_strucname, + 12) != 0 ) { + sts = printmsg( MOUNT_INCONVOL, 0, + 12, vcbdev->home.hm2$t_volname, + dname, + 12, vcbdev->home.hm2$t_strucname, + 12, vcb->vcbdev[0].home.hm2$t_strucname ); + break; + } + } + /* All volset members */ + + if( memcmp(vcbdev->home.hm2$t_volname, + volsets[device+1].vsr$t_label, 12 ) != 0 ) { + sts = printmsg( MOUNT_VOLOOO, 0, device+1, + 12, vcb->vcbdev[0].home.hm2$t_strucname, + 12, volsets[device+1].vsr$t_label, + dname, + 12, vcbdev->home.hm2$t_volname ); + break; + } + } /* rvn != 0 */ + + /* All volumes */ + + if( vcb->status & VCB_WRITE ) { +#if DEBUG + struct SCB scb; + if( $FAILS(sts = get_scb( vcb, device, &scb )) ) + break; + + 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 ); +#else + if( $FAILS(sts = get_scb( vcb, device, NULL )) ) + break; +#endif + } + if( $SUCCESSFUL(sts) && (flags & MOU_LOG) ) { + sts = printmsg( MOUNT_MOUNTED, 0, + 12, vcbdev->home.hm2$t_volname, vcbdev->dev->devnam ); + } + } /* device len */ + } /* for( each device ) */ + if( $FAILED(sts) ) { /* Dismount any volumes prior to the error */ + vcbdev = vcb->vcbdev; + for( device = 0; device < devices; device++, vcbdev++ ) { + if (vcbdev->dev == NULL) { + continue; + } + + if( vcb->status & VCB_WRITE && vcbdev->mapfcb != NULL ) { + /* check status? */ + deaccessfile(vcbdev->mapfcb); + vcbdev->idxfcb->status &= ~FCB_WRITE; + vcbdev->mapfcb = NULL; + } + while( vcb->dircache ) + cache_delete( (struct CACHE *) vcb->dircache ); + cache_remove( &vcb->fcb->cache ); + if( vcbdev->idxfcb != NULL ) { + /* check status? = */ + deaccesshead( vcbdev->idxfcb->headvioc, + vcbdev->idxfcb->head, + vcbdev->idxfcb->headvbn ); + vcbdev->idxfcb->headvioc = NULL; + cache_untouch( &vcbdev->idxfcb->cache, FALSE ); + vcbdev->idxfcb->status &= ~FCB_WRITE; + vcbdev->idxfcb = NULL; + } + vcbdev->dev->vcb = NULL; + virt_close( vcbdev->dev ); + } + cache_remove( &vcb->fcb->cache ); + } + } + + /* Wrap up */ + + if( $SUCCESSFUL(sts) && (flags & MOU_LOG) && + F11WORD(vcb->vcbdev[0].home.hm2$w_rvn) != 0 ) { + sts = printmsg ( MOUNT_VSMOUNTED, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); + } + if( vcb != NULL ) { + struct VCB *lp; + + if( $SUCCESSFUL(sts) ) { + for( lp = (struct VCB *)&vcb_list; + lp->next != NULL; lp = lp->next ) + ; + lp->next = vcb; + vcb->next = NULL; + } else { + free( vcb ); + vcb = NULL; + } + } + if( volsets != NULL ) + free( volsets ); + + if( $SUCCESSFUL(sts) ) + (void) mountdef( vcb->vcbdev[0].dev->devnam ); + + return sts; +} + +/***************************************************************** get_scb() */ +static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *retscb ) { + vmscond_t sts; + int mapopen = 1; + struct fiddef mapfid = { FID$C_BITMAP, FID$C_BITMAP, 0, 0 }; + struct VCBDEV *vcbdev = NULL; + struct VIOC *vioc = NULL; + struct SCB *scb = NULL; + int volwrt; + + if( retscb != NULL ) + memset( retscb, 0, sizeof( struct SCB ) ); + + vcbdev = vcb->vcbdev + device; + + mapfid.fid$b_rvn = (f11byte) F11WORD(vcbdev->home.hm2$w_rvn); + + volwrt = (vcb->status & VCB_WRITE) != 0; + + if( vcbdev->mapfcb == NULL ) { + mapopen = 0; + + if( $FAILS(sts = accessfile( vcb, &mapfid, + &vcbdev->mapfcb, volwrt )) ) { + vcb->status &= ~VCB_WRITE; + vcbdev->mapfcb = NULL; + printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); + return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); + } + } + + if( $FAILS(sts = accesschunk( vcbdev->mapfcb, 1, &vioc, + (char **) &scb, NULL, 0 )) ) { + if( !mapopen ) { + deaccessfile( vcbdev->mapfcb ); + vcbdev->mapfcb = NULL; + vcb->status &= ~VCB_WRITE; + } + printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); + return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); + } + + if( retscb != NULL ) + memcpy( retscb, scb, sizeof( struct SCB ) ); + + if( (F11WORD(scb->scb$w_checksum) != checksum( (f11word *)scb )) || + (scb->scb$w_cluster != vcbdev->home.hm2$w_cluster) ) { + deaccesschunk(vioc, 0, 0, FALSE); + if( !mapopen ) { + deaccessfile( vcbdev->mapfcb ); + vcbdev->mapfcb = NULL; + vcb->status &= ~VCB_WRITE; + } + printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); + return printmsg( MOUNT_BADSCB, MSG_CONTINUE, MOUNT_BITMAPSYS ); + } + + 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) ) -1; + deaccesschunk(vioc, 0, 0, FALSE); + + if( !volwrt || !mapopen ) { + if( $FAILS(sts = update_freecount( vcbdev, + &vcbdev->free_clusters )) ) { + printmsg( MOUNT_BITUPDFAIL, 0, vcbdev->dev->devnam ); + sts = printmsg( sts, MSG_CONTINUE, MOUNT_BITUPDFAIL ); + } + if( !(mapopen || volwrt) ) { + deaccessfile( vcbdev->mapfcb ); + vcbdev->mapfcb = NULL; + } + } + + return sts; +} + +/***************************************************************** show_volumes */ +/* First, some private helper routines */ + +/********************************************************** print_volhdr */ +static void print_volhdr( int volset, size_t devwid ) { + size_t i; + + if( volset ) + printf( " RVN " ); + else + printf( " " ); + printf( +"%-*s Volume/User Lvl Clust Owner/CreateDate VolProt/Default FileProt\n", + (int)devwid, "Dev" ); + if( volset ) + printf( " --- " ); + else + printf( " " ); + + for( i= 0; i < devwid; i++ ) + putchar( '-' ); + printf( +" ------------ --- ----- ----------------- ---------------------------\n" ); + + return; +} + +/********************************************************** print_prot() */ +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; + + w = 27; + + for( g = 0; g < 4; g++ ) { + w -= printf( "%c:", grp[g] ); + for( b = 0; b < 4; b++ ) { + if( !(code & (1<<(b + (4*g)))) ) { + putchar( acc[vol][b] ); + w--; + } + } + if( g < 3 ) { + putchar( ',' ); + w--; + } + } + while( w-- ) putchar( ' ' ); +} + +/********************************************************** print_volinf() */ +static void print_volinf( struct VCB *vcb, + size_t devwid, + unsigned device, + size_t wrap ) { + struct VCBDEV *vcbdev; + struct SCB scb; + vmscond_t sts; + size_t n; + f11word timbuf[7]; + char uicbuf[ 1+6+1+6+1+1], *p; + + vcbdev = vcb->vcbdev+device; + + if( $FAILS(sts = get_scb( vcb, device, &scb )) ) { + if( $MATCHCOND(sts, SS$_BADPARAM) ) + printf(" SCB is invalid\n" ); + else + printf( " *Unable to read SCB: %s\n", getmsg( sts, MSG_TEXT ) ); + } + + for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) { + if( vcbdev->dev->devnam[n] == ':' ) + break; + putchar( vcbdev->dev->devnam[n] ); + } + printf( "%*s ", (int)(devwid-n), "" ); + + printf( "%12.12s", vcbdev->home.hm2$t_volname ); + + (void) snprintf(uicbuf, sizeof(uicbuf), "[%6o,%6o]", + F11WORD( vcbdev->home.hm2$w_volowner.uic$w_grp ), + F11WORD( vcbdev->home.hm2$w_volowner.uic$w_mem )); + for( p = uicbuf; p[1] == ' '; p++ ) { + p[0] = ' '; p[1] = '['; + } + p = strchr( uicbuf, ',' ); + n = strspn( ++p, " " ); + memmove( p, p + n, 7 - n ); + memset( p + 7 - n, ' ', n ); + + printf( " %u.%u %5u %s", + F11WORD( vcbdev->home.hm2$w_struclev ) >> 8, + F11WORD( vcbdev->home.hm2$w_struclev ) & 0xFF, + F11WORD( vcbdev->home.hm2$w_cluster ), + uicbuf + ); + printf( " " ); + print_prot( F11WORD( vcbdev->home.hm2$w_protect ), 1 ); + if( !(vcb->status & VCB_WRITE) ) + printf( " write-locked" ); + + if( $SUCCESSFUL(sts) ) { + 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( $SUCCESSFUL(sys_numtim( timbuf, vcbdev->home.hm2$q_credate )) ) { + static const char *months = + "-JAN-FEB-MAR-APR-MAY-JUN-JUL-AUG-SEP-OCT-NOV-DEC-"; + + printf( "%2u%.5s%4u %2u:%2u ", timbuf[2], + months+(4*((size_t)timbuf[1]-1)), timbuf[0], + timbuf[3], timbuf[4]); + } else + printf( "%*s", 41-23, "" ); + + print_prot( F11WORD( vcbdev->home.hm2$w_fileprot ), 0 ); + + if( $SUCCESSFUL(sts) ) { + 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' ); +} + +/********************************************************** show_volumes() */ +void show_volumes( void ) { + struct VCB *vcb; + size_t maxd = sizeof( "Dev" ) -1; + int nvol = 0; + unsigned device; + + if( vcb_list == NULL ) { + printf( " No volumes mounted\n" ); + return; + } + + for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { + struct VCBDEV *vcbdev; + + if( vcb->devices == 0 ) { + printf( "No devices for volume\n" ); + abort(); + } + nvol++; + vcbdev = vcb->vcbdev; + for( device = 0; device < vcb->devices; device++, vcbdev++ ) { + size_t n; + for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) + if( vcbdev->dev->devnam[n] == ':' ) + break; + if( n > maxd ) + maxd = n; + } + } + for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { + struct VCBDEV *vcbdev; + unsigned device; + + vcbdev = vcb->vcbdev; + if( F11WORD(vcbdev->home.hm2$w_rvn) == 0 ) + continue; + + nvol--; + printf( " Volume set %12.12s\n", vcbdev->home.hm2$t_strucname ); + + print_volhdr( TRUE, maxd ); + + for( device = 0; device < vcb->devices; device++, vcbdev++ ) { + printf( " %3d ", F11WORD(vcbdev->home.hm2$w_rvn) ); + print_volinf( vcb, maxd, device, 8 ); + } + } + if( nvol == 0 ) + return; + + printf( "\n" ); + + print_volhdr( FALSE, maxd ); + for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { + struct VCBDEV *vcbdev; + unsigned device; + + vcbdev = vcb->vcbdev; + if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) + continue; + + vcbdev = vcb->vcbdev; + for( device = 0; device < vcb->devices; device++, vcbdev++ ) { + printf( " " ); + print_volinf( vcb, maxd, device, 2 ); + } + } + + return; +} + +/*************************************************************** acccess_rundown() */ +void access_rundown( void ) { + struct VCB *vcb, *next = NULL; + vmscond_t sts; + + for( vcb = vcb_list; vcb != NULL; vcb = next ) { + next = vcb->next; + + if( $FAILS(sts = dismount( vcb, 0 )) ) { + printf( "Dismount failed in rundown: %s\n", getmsg(sts, MSG_TEXT) ); + } + } +} + diff --git a/extracters/ods2/access.h b/extracters/ods2/access.h index 2229f3b..6c20634 100644 --- a/extracters/ods2/access.h +++ b/extracters/ods2/access.h @@ -1,161 +1,161 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. -*/ - -#ifndef _ACCESS_H -#define _ACCESS_H - -#include "ods2.h" -#include "cache.h" -#include "f11def.h" -#include "vmstime.h" - -#define EXTMAX 20 - -struct WCB { - struct CACHE cache; - uint32_t loblk, hiblk; /* Range of window */ - uint32_t hd_basevbn; /* File blocks prior to header */ - unsigned hd_seg_num; /* Header segment number */ - struct fiddef hd_fid; /* Header FID */ - unsigned short extcount; /* Extents in use */ - uint32_t phylen[EXTMAX]; - uint32_t phyblk[EXTMAX]; - uint8_t rvn[EXTMAX]; -}; /* Window control block */ - -#define VIOC_CHUNKSIZE 4 - -struct VIOC { - struct CACHE cache; - struct FCB *fcb; /* File this chunk is for */ - unsigned wrtmask; /* Bit mask for writable blocks */ - unsigned modmask; /* Bit mask for modified blocks */ - char data[VIOC_CHUNKSIZE][512]; /* Chunk data */ -}; /* Chunk of a file */ - -struct FCB { - struct CACHE cache; - struct VCB *vcb; /* Volume this file is for */ - struct VIOC *headvioc; /* Index file chunk for file header */ - struct HEAD *head; /* Pointer to header block */ - struct WCB *wcb; /* Window control block tree */ - struct VIOC *vioc; /* Virtual I/O chunk tree */ - uint32_t headvbn; /* vbn for file header */ - uint32_t hiblock; /* Highest block mapped */ - uint32_t highwater; /* First high water block */ - unsigned char status; /* FCB status bits */ -#define FCB_WRITE 1 /* FCB open for write... */ -#define FCB_WRITTEN 2 /* Modified */ -#define FCB_RDTSET 4 /* Revision date set by create */ - - uint8_t rvn; /* Initial file relative volume */ -}; /* File control block */ - -struct DIRCACHE { - struct CACHE cache; - struct fiddef parent; - struct dir$r_ent entry; - struct dir$r_rec record; -}; - -#define VCB_WRITE 1 -struct VCBDEV { - struct DEV *dev; /* Pointer to device info */ - struct FCB *idxfcb; /* Index file control block */ - struct FCB *mapfcb; /* Bitmap file control block */ - uint32_t clustersize; /* Cluster size of the device */ - uint32_t max_cluster; /* Total clusters on the device */ - uint32_t free_clusters; /* Free clusters on disk volume */ - struct HOME home; /* Volume home block */ -}; -struct VCB { - struct VCB *next; /* Next in mounted volume list (Must be first item) */ - unsigned status; /* Volume status */ - unsigned devices; /* Number of volumes in set */ - struct FCB *fcb; /* File control block tree */ - struct DIRCACHE *dircache; /* Directory cache tree */ - struct VCBDEV vcbdev[1]; /* List of volumes devices */ -}; /* Volume control block */ - -extern struct VCB *vcb_list; -void show_volumes( void ); - -/* RVN_TO_DEV( vcb, rvn ) - returns device from relative volume number */ - -/* returns NULL if RVN illegal or device not mounted */ - -#define RVN_TO_DEV( vcb, rvn ) ( ( rvn < 2 ) ? \ - vcb->vcbdev : \ - ( ( rvn <= vcb->devices ) ? \ - &vcb->vcbdev[rvn - 1] : \ - NULL \ - ) \ - ) - -struct NEWFILE { - struct RECATTR recattr; - VMSTIME credate; - VMSTIME revdate; - VMSTIME expdate; - VMSTIME bakdate; - f11word verlimit; - f11word revision; - uint32_t fileprot; - f11long filechar; - struct UIC fileowner; -}; - -void fid_copy( struct fiddef *dst, struct fiddef *src, unsigned rvn ); - -vmscond_t dismount( struct VCB *vcb, options_t options); -vmscond_t mount( options_t flags, unsigned devices, char *devnam[], char *label[] ); - -vmscond_t accesserase( struct VCB *vcb, struct fiddef *fid ); -vmscond_t deaccessfile( struct FCB *fcb ); -vmscond_t accessfile( struct VCB *vcb, struct fiddef *fid, - struct FCB **fcb, unsigned wrtflg ); - -vmscond_t deaccesschunk( struct VIOC *vioc, uint32_t wrtvbn, int wrtblks, int reuse ); -vmscond_t accesschunk( struct FCB *fcb, uint32_t vbn, struct VIOC **retvioc, - char **retbuff, uint32_t *retblocks, uint32_t wrtblks ); -vmscond_t access_extend( struct FCB *fcb, uint32_t blocks, unsigned contig ); - -vmscond_t deallocfile(struct FCB *fcb); - -f11word checksumn( f11word *block, int count ); -f11word checksum( f11word *block ); - -void access_rundown( void ); - -/* These live in update.c, which contains the write part of access */ - -vmscond_t update_freecount( struct VCBDEV *vcbdev, uint32_t *retcount ); -vmscond_t update_create( struct VCB *vcb, struct fiddef *did, char *filename, - struct fiddef *fid, struct NEWFILE *attrs, struct FCB **fcb ); -vmscond_t update_extend( struct FCB *fcb, uint32_t blocks, unsigned contig ); - -vmscond_t update_truncate( struct FCB *fcb, uint32_t newsize ); - -/* These are used by update.c */ - -vmscond_t deaccesshead( struct VIOC *vioc, struct HEAD *head, uint32_t idxblk ); -vmscond_t accesshead( struct VCB *vcb, struct fiddef *fid, uint32_t seg_num, - struct VIOC **vioc, struct HEAD **headbuff, - uint32_t *retidxblk, unsigned wrtflg ); -vmscond_t getwindow( struct FCB * fcb, uint32_t vbn, struct VCBDEV **devptr, - uint32_t *phyblk, uint32_t *phylen, struct fiddef *hdrfid, - uint32_t *hdrseq ); - -#endif /* # ifndef _ACCESS_H */ +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. +*/ + +#ifndef _ACCESS_H +#define _ACCESS_H + +#include "ods2.h" +#include "cache.h" +#include "f11def.h" +#include "vmstime.h" + +#define EXTMAX 20 + +struct WCB { + struct CACHE cache; + uint32_t loblk, hiblk; /* Range of window */ + uint32_t hd_basevbn; /* File blocks prior to header */ + unsigned hd_seg_num; /* Header segment number */ + struct fiddef hd_fid; /* Header FID */ + unsigned short extcount; /* Extents in use */ + uint32_t phylen[EXTMAX]; + uint32_t phyblk[EXTMAX]; + uint8_t rvn[EXTMAX]; +}; /* Window control block */ + +#define VIOC_CHUNKSIZE 4 + +struct VIOC { + struct CACHE cache; + struct FCB *fcb; /* File this chunk is for */ + unsigned wrtmask; /* Bit mask for writable blocks */ + unsigned modmask; /* Bit mask for modified blocks */ + char data[VIOC_CHUNKSIZE][512]; /* Chunk data */ +}; /* Chunk of a file */ + +struct FCB { + struct CACHE cache; + struct VCB *vcb; /* Volume this file is for */ + struct VIOC *headvioc; /* Index file chunk for file header */ + struct HEAD *head; /* Pointer to header block */ + struct WCB *wcb; /* Window control block tree */ + struct VIOC *vioc; /* Virtual I/O chunk tree */ + uint32_t headvbn; /* vbn for file header */ + uint32_t hiblock; /* Highest block mapped */ + uint32_t highwater; /* First high water block */ + unsigned char status; /* FCB status bits */ +#define FCB_WRITE 1 /* FCB open for write... */ +#define FCB_WRITTEN 2 /* Modified */ +#define FCB_RDTSET 4 /* Revision date set by create */ + + uint8_t rvn; /* Initial file relative volume */ +}; /* File control block */ + +struct DIRCACHE { + struct CACHE cache; + struct fiddef parent; + struct dir$r_ent entry; + struct dir$r_rec record; +}; + +#define VCB_WRITE 1 +struct VCBDEV { + struct DEV *dev; /* Pointer to device info */ + struct FCB *idxfcb; /* Index file control block */ + struct FCB *mapfcb; /* Bitmap file control block */ + uint32_t clustersize; /* Cluster size of the device */ + uint32_t max_cluster; /* Total clusters on the device */ + uint32_t free_clusters; /* Free clusters on disk volume */ + struct HOME home; /* Volume home block */ +}; +struct VCB { + struct VCB *next; /* Next in mounted volume list (Must be first item) */ + unsigned status; /* Volume status */ + unsigned devices; /* Number of volumes in set */ + struct FCB *fcb; /* File control block tree */ + struct DIRCACHE *dircache; /* Directory cache tree */ + struct VCBDEV vcbdev[1]; /* List of volumes devices */ +}; /* Volume control block */ + +extern struct VCB *vcb_list; +void show_volumes( void ); + +/* RVN_TO_DEV( vcb, rvn ) - returns device from relative volume number */ + +/* returns NULL if RVN illegal or device not mounted */ + +#define RVN_TO_DEV( vcb, rvn ) ( ( rvn < 2 ) ? \ + vcb->vcbdev : \ + ( ( rvn <= vcb->devices ) ? \ + &vcb->vcbdev[rvn - 1] : \ + NULL \ + ) \ + ) + +struct NEWFILE { + struct RECATTR recattr; + VMSTIME credate; + VMSTIME revdate; + VMSTIME expdate; + VMSTIME bakdate; + f11word verlimit; + f11word revision; + uint32_t fileprot; + f11long filechar; + struct UIC fileowner; +}; + +void fid_copy( struct fiddef *dst, struct fiddef *src, unsigned rvn ); + +vmscond_t dismount( struct VCB *vcb, options_t options); +vmscond_t mount( options_t flags, unsigned devices, char *devnam[], char *label[] ); + +vmscond_t accesserase( struct VCB *vcb, struct fiddef *fid ); +vmscond_t deaccessfile( struct FCB *fcb ); +vmscond_t accessfile( struct VCB *vcb, struct fiddef *fid, + struct FCB **fcb, unsigned wrtflg ); + +vmscond_t deaccesschunk( struct VIOC *vioc, uint32_t wrtvbn, int wrtblks, int reuse ); +vmscond_t accesschunk( struct FCB *fcb, uint32_t vbn, struct VIOC **retvioc, + char **retbuff, uint32_t *retblocks, uint32_t wrtblks ); +vmscond_t access_extend( struct FCB *fcb, uint32_t blocks, unsigned contig ); + +vmscond_t deallocfile(struct FCB *fcb); + +f11word checksumn( f11word *block, int count ); +f11word checksum( f11word *block ); + +void access_rundown( void ); + +/* These live in update.c, which contains the write part of access */ + +vmscond_t update_freecount( struct VCBDEV *vcbdev, uint32_t *retcount ); +vmscond_t update_create( struct VCB *vcb, struct fiddef *did, char *filename, + struct fiddef *fid, struct NEWFILE *attrs, struct FCB **fcb ); +vmscond_t update_extend( struct FCB *fcb, uint32_t blocks, unsigned contig ); + +vmscond_t update_truncate( struct FCB *fcb, uint32_t newsize ); + +/* These are used by update.c */ + +vmscond_t deaccesshead( struct VIOC *vioc, struct HEAD *head, uint32_t idxblk ); +vmscond_t accesshead( struct VCB *vcb, struct fiddef *fid, uint32_t seg_num, + struct VIOC **vioc, struct HEAD **headbuff, + uint32_t *retidxblk, unsigned wrtflg ); +vmscond_t getwindow( struct FCB * fcb, uint32_t vbn, struct VCBDEV **devptr, + uint32_t *phyblk, uint32_t *phylen, struct fiddef *hdrfid, + uint32_t *hdrseq ); + +#endif /* # ifndef _ACCESS_H */ diff --git a/extracters/ods2/cache.c b/extracters/ods2/cache.c index 4cd94a9..8469b05 100644 --- a/extracters/ods2/cache.c +++ b/extracters/ods2/cache.c @@ -1,451 +1,451 @@ -/* Cache.c Caching control 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -/* - * The theory is that all cachable objects share a common cache pool. - * Any object with a reference count of zero is a candidate for - * destruction. All cacheable objects have a 'struct CACHE' as the - * first item of the object so that cache pointers and object pointers - * are 'interchangeable'. All cache objects are also part of a binary - * tree so that they can be located. There are many instances of these - * binary trees: for files on a volume, file windows, file chunks - * (segments) etc. Also each object can have an object manager: a - * routine to handle special object deletion requirements (for example - * to remove all file chunks before removing a file), or to flush - * objects (write modified chunks to disk). - * - * The main routines is cache_find() which is used to search for and - * place objects in a binary tree which is located by a tree pointer. - * cache_touch() and cache_untouch() are used to bump and decrement - * reference counts. Any object with a reference count of zero is a - * candidate for destruction - but if it requires special action - * before it is destroyed, such as deleting a subtree, flushing data - * to disk, etc, then it should have an 'object manager' function - * assigned which will be called at deletion time to take care of these - * needs. - * - * This version of the cache routines attempts to maintain binary tree - * balance by dynamically changing tree shape during search functions. - * All objects remain in the binary tree until destruction so that they - * can be re-used at any time. Objects with a zero reference count are - * special in that they are kept in a 'least recently used' linked list. - * When the reference count is decemented a flag is used to indicate - * whether the object is likely to be referenced again so that the object - * can be put in the 'correct' end of this list. - * - * Note: These routines are 'general' in that do not know anything - * 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" - -#define IMBALANCE 5 /* Tree imbalance limit */ -#define CACHELIM 256 /* Free object limit */ -#define CACHEGOAL 128 /* Free object goal */ - -static int cachefinds = 0; -static int cachecreated = 0; -static int cachepurges = 0; -static int cachepeak = 0; -static int cachecount = 0; -static int cachefreecount = 0; -static int cachedeletes = 0; - -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 */ - -void cache_show(void) -{ - printf("CACHE_SHOW Find %d Create %d Purge %d Peak %d Count %d Free %d\n", - cachefinds, cachecreated, cachepurges, cachepeak, cachecount, - cachefreecount); - if (cachecreated - cachedeletes != cachecount) { - printf(" - Deleted %d\n", cachedeletes); - } -} - -/*********************************************************** cache_refcount() */ - -/* cache_refcount() - compute reference count for cache subtree... */ - -int cache_refcount( struct CACHE *cacheobj ) { - register int refcount = 0; - - if( cacheobj != NULL ) { - refcount = cacheobj->refcount; - if (cacheobj->left != NULL) { - refcount += cache_refcount(cacheobj->left); - } - if (cacheobj->right != NULL) { - refcount += cache_refcount(cacheobj->right); - } - } - return refcount; -} - -/************************************************************* cache_delete() */ - -/* cache_delete() - blow away item from cache - allow item to select a proxy (!) - and adjust 'the tree' containing the item. */ - -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 ) { - if( actualobj != NULL ) - *actualobj = NULL; - return; - } - if (proxyobj == cacheobj) break; - cacheobj = proxyobj; - } -#if DEBUG - if (cachedeleteing) - printf("CACHE deletion while delete in progress\n"); -#endif - if (cacheobj->refcount != 0) { -#if DEBUG - printf("CACHE attempt to delete referenced object %d:%d\n", - cacheobj->objtype, cacheobj->hashval); -#endif - abort(); - if( actualobj != NULL ) - *actualobj = NULL; - return; - } - cacheobj->lastlru->nextlru = cacheobj->nextlru; - cacheobj->nextlru->lastlru = cacheobj->lastlru; - if (cacheobj->left == NULL) { - if (cacheobj->right == NULL) { - *(cacheobj->parent) = NULL; - } else { - cacheobj->right->parent = cacheobj->parent; - *(cacheobj->parent) = cacheobj->right; - } - } else { - if (cacheobj->right == NULL) { - cacheobj->left->parent = cacheobj->parent; - *(cacheobj->parent) = cacheobj->left; - } else { - register struct CACHE *path = cacheobj->right; - if (path->left != NULL) { - do { - path = path->left; - } while (path->left != NULL); - *(path->parent) = path->right; - if (path->right != NULL) path->right->parent = path->parent; - path->right = cacheobj->right; - path->right->parent = &path->right; - } - path->left = cacheobj->left; - path->left->parent = &path->left; - path->parent = cacheobj->parent; - *(cacheobj->parent) = path; - path->balance = 0; - } - } - cachecount--; - cachefreecount--; - cachedeletes++; -#if DEBUG - cacheobj->nextlru = NULL; - cacheobj->lastlru = NULL; - cacheobj->left = NULL; - cacheobj->right = NULL; - cacheobj->parent = NULL; - cacheobj->objmanager = NULL; - cacheobj->hashval = 0; - cacheobj->balance = 0; - cacheobj->refcount = 0; -#endif - 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( int all ) { - if (!cachedeleteing) { - register struct CACHE *cacheobj; - - cacheobj = lrulist.lastlru; - cachepurges++; - - while( (all || cachefreecount > CACHEGOAL) && cacheobj != &lrulist ) { - register struct CACHE *lastobj; - - lastobj = cacheobj->lastlru; -#if DEBUG - if (cacheobj->lastlru->nextlru != cacheobj || - cacheobj->nextlru->lastlru != cacheobj || - *(cacheobj->parent) != cacheobj) { - printf("CACHE pointers in bad shape!\n"); - } -#endif - if (cacheobj->refcount == 0) { - struct CACHE *actualobj; - cache_deleter( cacheobj, &actualobj ); - if (actualobj != lastobj) cacheobj = lastobj; - } else { - cacheobj = lastobj; - } - } - } -} - -/************************************************************** cache_flush() */ - -/* cache_flush() - flush modified entries in cache */ - -void cache_flush(void) { - register struct CACHE *cacheobj; - - for( cacheobj = lrulist.lastlru; - cacheobj != &lrulist; - cacheobj = cacheobj->lastlru ) { - - if (cacheobj->objmanager != NULL) { - (*cacheobj->objmanager) (cacheobj, TRUE); - } - } -} - -/************************************************************* cache_remove() */ - -/* cache_remove() - delete all possible objects from cache subtree */ - -void cache_remove( struct CACHE *cacheobj ) { - if (cacheobj != NULL) { - if (cacheobj->left != NULL) - cache_remove(cacheobj->left); - if (cacheobj->right != NULL) - cache_remove(cacheobj->right); - if (cacheobj->refcount == 0) { - struct CACHE *delobj; - do { - cache_deleter(cacheobj, &delobj); - } while (delobj != NULL && delobj != cacheobj); - } - } -} - -/************************************************************** cache_touch() */ - -/* cache_touch() - to increase the access count on an object... */ - -void cache_touch(struct CACHE *cacheobj) { - if (cacheobj->refcount++ == 0) { -#if DEBUG - if (cacheobj->nextlru == NULL || cacheobj->lastlru == NULL) { - printf("CACHE LRU pointers corrupt!\n"); - abort(); - } -#endif - cacheobj->nextlru->lastlru = cacheobj->lastlru; - cacheobj->lastlru->nextlru = cacheobj->nextlru; - cacheobj->nextlru = NULL; - cacheobj->lastlru = NULL; - cachefreecount--; - } -} - -/************************************************************ cache_untouch() */ - -/* Deaccess an object. - * Recycle => TRUE puts object at front of LRU list, indicating that - * it's likely to be reused soon. Note that untouch() can - * trigger a purge, so the object can be deleted on return. - * This won't happen if recycle is TRUE because purge will maintain - * CACHEGOAL LRU entries, which must be non-zero. - */ - -void cache_untouch( struct CACHE *cacheobj, int recycle ) { - if( cacheobj->refcount > 0 ) { - if( --cacheobj->refcount == 0 ) { -#if DEBUG - if( cacheobj->nextlru != NULL || cacheobj->lastlru != NULL ) { - printf("CACHE LRU pointers corrupt\n"); - } -#endif - if( recycle ) { - cacheobj->nextlru = lrulist.nextlru; - cacheobj->lastlru = &lrulist; - cacheobj->nextlru->lastlru = cacheobj; - lrulist.nextlru = cacheobj; - } else { - cacheobj->lastlru = lrulist.lastlru; - cacheobj->nextlru = &lrulist; - cacheobj->lastlru->nextlru = cacheobj; - lrulist.lastlru = cacheobj; - } - if( ++cachefreecount >= CACHELIM ) - cache_purge(FALSE); - } - } else { -#if DEBUG - printf("CACHE untouch limit exceeded\n"); -#endif - abort(); - } -} - -/*************************************************************** cache_find() */ - -/* cache_find() - to find or create cache entries... - * - * The grand plan here was to use a hash code as a quick key - * and call a compare function for duplicates. So far no data - * type actually works like this - they either have a unique binary - * key, or all records rely on the compare function - sigh! - * Never mind, the potential is there! :-) - * - * This version will call a creation function to allocate and - * initialize an object if it is not found. - */ - -void *cache_find( void **root, uint32_t hashval, void *keyval, vmscond_t *retsts, - int (*compare_func) ( uint32_t hashval, void *keyval, - void *node ), - void *(*create_func) ( uint32_t hashval, void *keyval, - vmscond_t *retsts ) ) { - - register struct CACHE *cacheobj, **parent; - - parent = (struct CACHE **) root; - cachefinds++; - - while( (cacheobj = *parent) != NULL ) { - register int cmp; - - cmp = hashval - cacheobj->hashval; -#if DEBUG - if (cacheobj->parent != parent) { - printf("CACHE Parent pointer is corrupt\n"); - } -#endif - if (cmp == 0 && compare_func != NULL) { - cmp = (*compare_func) (hashval, keyval, cacheobj); - } - if (cmp == 0) { - cache_touch(cacheobj); - if (retsts != NULL) - *retsts = SS$_NORMAL; - return cacheobj; - } - if (cmp < 0) { -#ifdef IMBALANCE - register struct CACHE *left_path; - - left_path = cacheobj->left; - if (left_path != NULL && cacheobj->balance-- < -IMBALANCE) { - cacheobj->left = left_path->right; - if (cacheobj->left != NULL) { - cacheobj->left->parent = &cacheobj->left; - } - left_path->right = cacheobj; - cacheobj->parent = &left_path->right; - *parent = left_path; - left_path->parent = parent; - cacheobj->balance = 0; - } else { - parent = &cacheobj->left; - } - } else { - register struct CACHE *right_path; - - right_path = cacheobj->right; - if (right_path != NULL && cacheobj->balance++ > IMBALANCE) { - cacheobj->right = right_path->left; - if (cacheobj->right != NULL) { - cacheobj->right->parent = &cacheobj->right; - } - right_path->left = cacheobj; - cacheobj->parent = &right_path->left; - *parent = right_path; - right_path->parent = parent; - cacheobj->balance = 0; - } else { - parent = &cacheobj->right; - } -#else - parent = &cacheobj->left; - } else { - parent = &cacheobj->right; -#endif - } - } - if (create_func == NULL) { - if (retsts != NULL) - *retsts = SS$_ITEMNOTFOUND; - } else { - cacheobj = (*create_func) (hashval, keyval, retsts); - if (cacheobj != NULL) { - cacheobj->nextlru = NULL; - cacheobj->lastlru = NULL; - cacheobj->left = NULL; - cacheobj->right = NULL; - cacheobj->parent = parent; - cacheobj->hashval = hashval; - cacheobj->refcount = 1; - cacheobj->balance = 0; - *parent = cacheobj; - cachecreated++; - if (cachecount++ >= cachepeak) - cachepeak = cachecount; - } - } - return cacheobj; -} +/* Cache.c Caching control 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +/* + * The theory is that all cachable objects share a common cache pool. + * Any object with a reference count of zero is a candidate for + * destruction. All cacheable objects have a 'struct CACHE' as the + * first item of the object so that cache pointers and object pointers + * are 'interchangeable'. All cache objects are also part of a binary + * tree so that they can be located. There are many instances of these + * binary trees: for files on a volume, file windows, file chunks + * (segments) etc. Also each object can have an object manager: a + * routine to handle special object deletion requirements (for example + * to remove all file chunks before removing a file), or to flush + * objects (write modified chunks to disk). + * + * The main routines is cache_find() which is used to search for and + * place objects in a binary tree which is located by a tree pointer. + * cache_touch() and cache_untouch() are used to bump and decrement + * reference counts. Any object with a reference count of zero is a + * candidate for destruction - but if it requires special action + * before it is destroyed, such as deleting a subtree, flushing data + * to disk, etc, then it should have an 'object manager' function + * assigned which will be called at deletion time to take care of these + * needs. + * + * This version of the cache routines attempts to maintain binary tree + * balance by dynamically changing tree shape during search functions. + * All objects remain in the binary tree until destruction so that they + * can be re-used at any time. Objects with a zero reference count are + * special in that they are kept in a 'least recently used' linked list. + * When the reference count is decemented a flag is used to indicate + * whether the object is likely to be referenced again so that the object + * can be put in the 'correct' end of this list. + * + * Note: These routines are 'general' in that do not know anything + * 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" + +#define IMBALANCE 5 /* Tree imbalance limit */ +#define CACHELIM 256 /* Free object limit */ +#define CACHEGOAL 128 /* Free object goal */ + +static int cachefinds = 0; +static int cachecreated = 0; +static int cachepurges = 0; +static int cachepeak = 0; +static int cachecount = 0; +static int cachefreecount = 0; +static int cachedeletes = 0; + +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 */ + +void cache_show(void) +{ + printf("CACHE_SHOW Find %d Create %d Purge %d Peak %d Count %d Free %d\n", + cachefinds, cachecreated, cachepurges, cachepeak, cachecount, + cachefreecount); + if (cachecreated - cachedeletes != cachecount) { + printf(" - Deleted %d\n", cachedeletes); + } +} + +/*********************************************************** cache_refcount() */ + +/* cache_refcount() - compute reference count for cache subtree... */ + +int cache_refcount( struct CACHE *cacheobj ) { + register int refcount = 0; + + if( cacheobj != NULL ) { + refcount = cacheobj->refcount; + if (cacheobj->left != NULL) { + refcount += cache_refcount(cacheobj->left); + } + if (cacheobj->right != NULL) { + refcount += cache_refcount(cacheobj->right); + } + } + return refcount; +} + +/************************************************************* cache_delete() */ + +/* cache_delete() - blow away item from cache - allow item to select a proxy (!) + and adjust 'the tree' containing the item. */ + +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 ) { + if( actualobj != NULL ) + *actualobj = NULL; + return; + } + if (proxyobj == cacheobj) break; + cacheobj = proxyobj; + } +#if DEBUG + if (cachedeleteing) + printf("CACHE deletion while delete in progress\n"); +#endif + if (cacheobj->refcount != 0) { +#if DEBUG + printf("CACHE attempt to delete referenced object %d:%d\n", + cacheobj->objtype, cacheobj->hashval); +#endif + abort(); + if( actualobj != NULL ) + *actualobj = NULL; + return; + } + cacheobj->lastlru->nextlru = cacheobj->nextlru; + cacheobj->nextlru->lastlru = cacheobj->lastlru; + if (cacheobj->left == NULL) { + if (cacheobj->right == NULL) { + *(cacheobj->parent) = NULL; + } else { + cacheobj->right->parent = cacheobj->parent; + *(cacheobj->parent) = cacheobj->right; + } + } else { + if (cacheobj->right == NULL) { + cacheobj->left->parent = cacheobj->parent; + *(cacheobj->parent) = cacheobj->left; + } else { + register struct CACHE *path = cacheobj->right; + if (path->left != NULL) { + do { + path = path->left; + } while (path->left != NULL); + *(path->parent) = path->right; + if (path->right != NULL) path->right->parent = path->parent; + path->right = cacheobj->right; + path->right->parent = &path->right; + } + path->left = cacheobj->left; + path->left->parent = &path->left; + path->parent = cacheobj->parent; + *(cacheobj->parent) = path; + path->balance = 0; + } + } + cachecount--; + cachefreecount--; + cachedeletes++; +#if DEBUG + cacheobj->nextlru = NULL; + cacheobj->lastlru = NULL; + cacheobj->left = NULL; + cacheobj->right = NULL; + cacheobj->parent = NULL; + cacheobj->objmanager = NULL; + cacheobj->hashval = 0; + cacheobj->balance = 0; + cacheobj->refcount = 0; +#endif + 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( int all ) { + if (!cachedeleteing) { + register struct CACHE *cacheobj; + + cacheobj = lrulist.lastlru; + cachepurges++; + + while( (all || cachefreecount > CACHEGOAL) && cacheobj != &lrulist ) { + register struct CACHE *lastobj; + + lastobj = cacheobj->lastlru; +#if DEBUG + if (cacheobj->lastlru->nextlru != cacheobj || + cacheobj->nextlru->lastlru != cacheobj || + *(cacheobj->parent) != cacheobj) { + printf("CACHE pointers in bad shape!\n"); + } +#endif + if (cacheobj->refcount == 0) { + struct CACHE *actualobj; + cache_deleter( cacheobj, &actualobj ); + if (actualobj != lastobj) cacheobj = lastobj; + } else { + cacheobj = lastobj; + } + } + } +} + +/************************************************************** cache_flush() */ + +/* cache_flush() - flush modified entries in cache */ + +void cache_flush(void) { + register struct CACHE *cacheobj; + + for( cacheobj = lrulist.lastlru; + cacheobj != &lrulist; + cacheobj = cacheobj->lastlru ) { + + if (cacheobj->objmanager != NULL) { + (*cacheobj->objmanager) (cacheobj, TRUE); + } + } +} + +/************************************************************* cache_remove() */ + +/* cache_remove() - delete all possible objects from cache subtree */ + +void cache_remove( struct CACHE *cacheobj ) { + if (cacheobj != NULL) { + if (cacheobj->left != NULL) + cache_remove(cacheobj->left); + if (cacheobj->right != NULL) + cache_remove(cacheobj->right); + if (cacheobj->refcount == 0) { + struct CACHE *delobj; + do { + cache_deleter(cacheobj, &delobj); + } while (delobj != NULL && delobj != cacheobj); + } + } +} + +/************************************************************** cache_touch() */ + +/* cache_touch() - to increase the access count on an object... */ + +void cache_touch(struct CACHE *cacheobj) { + if (cacheobj->refcount++ == 0) { +#if DEBUG + if (cacheobj->nextlru == NULL || cacheobj->lastlru == NULL) { + printf("CACHE LRU pointers corrupt!\n"); + abort(); + } +#endif + cacheobj->nextlru->lastlru = cacheobj->lastlru; + cacheobj->lastlru->nextlru = cacheobj->nextlru; + cacheobj->nextlru = NULL; + cacheobj->lastlru = NULL; + cachefreecount--; + } +} + +/************************************************************ cache_untouch() */ + +/* Deaccess an object. + * Recycle => TRUE puts object at front of LRU list, indicating that + * it's likely to be reused soon. Note that untouch() can + * trigger a purge, so the object can be deleted on return. + * This won't happen if recycle is TRUE because purge will maintain + * CACHEGOAL LRU entries, which must be non-zero. + */ + +void cache_untouch( struct CACHE *cacheobj, int recycle ) { + if( cacheobj->refcount > 0 ) { + if( --cacheobj->refcount == 0 ) { +#if DEBUG + if( cacheobj->nextlru != NULL || cacheobj->lastlru != NULL ) { + printf("CACHE LRU pointers corrupt\n"); + } +#endif + if( recycle ) { + cacheobj->nextlru = lrulist.nextlru; + cacheobj->lastlru = &lrulist; + cacheobj->nextlru->lastlru = cacheobj; + lrulist.nextlru = cacheobj; + } else { + cacheobj->lastlru = lrulist.lastlru; + cacheobj->nextlru = &lrulist; + cacheobj->lastlru->nextlru = cacheobj; + lrulist.lastlru = cacheobj; + } + if( ++cachefreecount >= CACHELIM ) + cache_purge(FALSE); + } + } else { +#if DEBUG + printf("CACHE untouch limit exceeded\n"); +#endif + abort(); + } +} + +/*************************************************************** cache_find() */ + +/* cache_find() - to find or create cache entries... + * + * The grand plan here was to use a hash code as a quick key + * and call a compare function for duplicates. So far no data + * type actually works like this - they either have a unique binary + * key, or all records rely on the compare function - sigh! + * Never mind, the potential is there! :-) + * + * This version will call a creation function to allocate and + * initialize an object if it is not found. + */ + +void *cache_find( void **root, uint32_t hashval, void *keyval, vmscond_t *retsts, + int (*compare_func) ( uint32_t hashval, void *keyval, + void *node ), + void *(*create_func) ( uint32_t hashval, void *keyval, + vmscond_t *retsts ) ) { + + register struct CACHE *cacheobj, **parent; + + parent = (struct CACHE **) root; + cachefinds++; + + while( (cacheobj = *parent) != NULL ) { + register int cmp; + + cmp = hashval - cacheobj->hashval; +#if DEBUG + if (cacheobj->parent != parent) { + printf("CACHE Parent pointer is corrupt\n"); + } +#endif + if (cmp == 0 && compare_func != NULL) { + cmp = (*compare_func) (hashval, keyval, cacheobj); + } + if (cmp == 0) { + cache_touch(cacheobj); + if (retsts != NULL) + *retsts = SS$_NORMAL; + return cacheobj; + } + if (cmp < 0) { +#ifdef IMBALANCE + register struct CACHE *left_path; + + left_path = cacheobj->left; + if (left_path != NULL && cacheobj->balance-- < -IMBALANCE) { + cacheobj->left = left_path->right; + if (cacheobj->left != NULL) { + cacheobj->left->parent = &cacheobj->left; + } + left_path->right = cacheobj; + cacheobj->parent = &left_path->right; + *parent = left_path; + left_path->parent = parent; + cacheobj->balance = 0; + } else { + parent = &cacheobj->left; + } + } else { + register struct CACHE *right_path; + + right_path = cacheobj->right; + if (right_path != NULL && cacheobj->balance++ > IMBALANCE) { + cacheobj->right = right_path->left; + if (cacheobj->right != NULL) { + cacheobj->right->parent = &cacheobj->right; + } + right_path->left = cacheobj; + cacheobj->parent = &right_path->left; + *parent = right_path; + right_path->parent = parent; + cacheobj->balance = 0; + } else { + parent = &cacheobj->right; + } +#else + parent = &cacheobj->left; + } else { + parent = &cacheobj->right; +#endif + } + } + if (create_func == NULL) { + if (retsts != NULL) + *retsts = SS$_ITEMNOTFOUND; + } else { + cacheobj = (*create_func) (hashval, keyval, retsts); + if (cacheobj != NULL) { + cacheobj->nextlru = NULL; + cacheobj->lastlru = NULL; + cacheobj->left = NULL; + cacheobj->right = NULL; + cacheobj->parent = parent; + cacheobj->hashval = hashval; + cacheobj->refcount = 1; + cacheobj->balance = 0; + *parent = cacheobj; + cachecreated++; + if (cachecount++ >= cachepeak) + cachepeak = cachecount; + } + } + return cacheobj; +} diff --git a/extracters/ods2/cache.h b/extracters/ods2/cache.h index af0d570..40e976e 100644 --- a/extracters/ods2/cache.h +++ b/extracters/ods2/cache.h @@ -1,59 +1,59 @@ -/* Cache.h Definitions for cache 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#ifndef _CACHE_H -#define _CACHE_H - -#ifdef VAXC /* Stupid VAX C doesn't allow "signed" keyword */ -#define signed -#else -#define signed signed -#endif - -#include "ods2.h" - -struct CACHE { - struct CACHE *nextlru; /* next object on least recently used list */ - struct CACHE *lastlru; /* last object on least recently used list */ - struct CACHE *left; /* left branch of binary tree */ - struct CACHE *right; /* right branch of binary tree */ - struct CACHE **parent; /* address of pointer to this object */ - void *(*objmanager) ( struct CACHE * cacheobj, int flushonly ); - uint32_t hashval; /* object hash value */ - short refcount; /* object reference count */ - signed char balance; /* object tree imbalance factor */ - signed char objtype; /* object type (for debugging) */ -#define OBJTYPE_DEV 1 -#define OBJTYPE_FCB 2 -#define OBJTYPE_WCB 3 -#define OBJTYPE_VIOC 7 -#define OBJTYPE_DIR 8 -}; - -void cache_show(void); -int cache_refcount(struct CACHE *cacheobj); -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); -void cache_untouch(struct CACHE * cacheobj,int recycle); -void *cache_find( void **root, uint32_t hashval, void *keyval, vmscond_t *retsts, - int (*compare_func) ( uint32_t hashval, void *keyval, - void *node ), - void *(*create_func) ( uint32_t hashval, void *keyval, - vmscond_t *retsts ) ); - -#endif /* #ifndef _CACHE_H */ +/* Cache.h Definitions for cache 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#ifndef _CACHE_H +#define _CACHE_H + +#ifdef VAXC /* Stupid VAX C doesn't allow "signed" keyword */ +#define signed +#else +#define signed signed +#endif + +#include "ods2.h" + +struct CACHE { + struct CACHE *nextlru; /* next object on least recently used list */ + struct CACHE *lastlru; /* last object on least recently used list */ + struct CACHE *left; /* left branch of binary tree */ + struct CACHE *right; /* right branch of binary tree */ + struct CACHE **parent; /* address of pointer to this object */ + void *(*objmanager) ( struct CACHE * cacheobj, int flushonly ); + uint32_t hashval; /* object hash value */ + short refcount; /* object reference count */ + signed char balance; /* object tree imbalance factor */ + signed char objtype; /* object type (for debugging) */ +#define OBJTYPE_DEV 1 +#define OBJTYPE_FCB 2 +#define OBJTYPE_WCB 3 +#define OBJTYPE_VIOC 7 +#define OBJTYPE_DIR 8 +}; + +void cache_show(void); +int cache_refcount(struct CACHE *cacheobj); +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); +void cache_untouch(struct CACHE * cacheobj,int recycle); +void *cache_find( void **root, uint32_t hashval, void *keyval, vmscond_t *retsts, + int (*compare_func) ( uint32_t hashval, void *keyval, + void *node ), + void *(*create_func) ( uint32_t hashval, void *keyval, + vmscond_t *retsts ) ); + +#endif /* #ifndef _CACHE_H */ diff --git a/extracters/ods2/cmddef.h b/extracters/ods2/cmddef.h index 69437bf..dc2845f 100644 --- a/extracters/ods2/cmddef.h +++ b/extracters/ods2/cmddef.h @@ -1,206 +1,206 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - - -#ifndef _CMDDEF_H -#define _CMDDEF_H - -#ifndef _BSD_SOURCE -#define _BSD_SOURCE -#endif -#ifndef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE -#endif -#include -#include -#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 DT_NAME "medium_type" - -#define DECL_CMD(x) vmscond_t 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; - -typedef vmscond_t (*cmdproc_t) (int argc,char *argv[],int qualc,char *qualv[]); - -/* Command table entry */ - -struct CMDSET { - char *name; - cmdproc_t proc; - int uniq; - qualp_t validquals; - paramp_t params; - char *helpstr; -}; - -/* set, clear: bits specified are cleared, then set in value - * qtype = qualifier's value type. + => value required, - optional, 0 none - * Following macros simplify setting up the tables. - * helpstr: if null, switch not listed in help. If starts with -, - * listed as negatable in help. If null string (""), listed - * as value table. Otherwise, .hlp file lookup key. - */ -#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) -#define PV(val) PROT, ((void *)val) -#define UV(val) UIC, ((void *)val) - -/* Use VOPT(XX()) for optional value */ -#define VOPT(def) -def - -struct qual { - const char *name; - options_t set; - options_t clear; - enum qualtype { opt_optional = -1, NOVAL = 0, /* enum must be signed */ - KEYVAL, KEYCOL, DECVAL, STRVAL, PROT, UIC } qtype; - void *arg; - 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 = 1, OPT = 2, CND = 4, NOLIM = 8 } flags; - enum partype { NONE = 0, FSPEC, LIST, KEYWD, QUALS, STRING } ptype; -#define PA(arg) NULL, (arg) -#define NOPA PA(NULL) - hlpfunc_t *hlpfnc; - void *arg; - char *helpstr; -}; - -/* Default qualifer sets */ - -struct defquals { - struct defquals *next; - int qualc; - char **qualv; - char *data[1]; -}; - -typedef struct defquals defquals_t; -typedef defquals_t *defqualsp_t; - -extern const char *qstyle_s; -#define qstyle_c (qstyle_s[0]) - -extern int verify_cmd; -extern int error_exit; - -vmscond_t parselist( int *nret, char ***items, size_t min, char *arg ); -vmscond_t checkquals( options_t *result, options_t defval, - qualp_t qualset, int qualc,char **qualv ); - -int keycomp(const char *param, const char *keywrd); -vmscond_t confirm_cmd( vmscond_t status, ... ); - -vmscond_t sethelpfile( char *filename, char *env, char *pname ); -vmscond_t showhelpfile( void ); - -vmscond_t helptopic( options_t options, char *topic, ... ); -#define HLP_OPTIONAL 0x00000001 -#define HLP_WRAP 0x00000002 -#define HLP_RELOAD 0x00000004 -#define HLP_ABBREV 0x00000008 - -typedef struct pause { - size_t interval; - size_t lineno; - vmscond_t prompt; -} pause_t; - -vmscond_t put_c( int c, FILE *outf, pause_t *pause ); -vmscond_t put_str( const char *buf, FILE *outf, pause_t *pause ); -vmscond_t put_strn( const char *buf, size_t len, FILE *outf, pause_t *pause ); -void termchar( int *width, int *height ); - -vmscond_t prvmstime(FILE *of, VMSTIME vtime, const char *sfx); -void pwrap( FILE *of, int *pos, const char *fmt, ... ); - -#ifdef HELPFILEDEFS -/* File and index structures private to helpcmd and makehelp. */ - -/* Helpfile magic cookie, version & header */ - -#define HLP_MAGIC "ODS2HLP" -#define HLP_VERSION 1 - -typedef struct hlphdr { - char magic[ sizeof(HLP_MAGIC) ]; - uint32_t version; - uint32_t psize, ssize, tsize; - time_t ddate; - size_t size; -} hlphdr_t; - -/* In-memory pointer / file offsets */ - -typedef union ptr { - void *ptr; - ptrdiff_t ofs; -} ptr_t; - -/* Help tree topic nodes (including root) */ - -typedef struct hlproot { - ptr_t topics; /*struct hlptopic * */ - size_t ntopics; -} hlproot_t; - -/* Help topic data under each node */ - -typedef struct hlptopic { - ptr_t key; /* char * */ - ptr_t text; /* char * */ - hlproot_t subtopics; - uint32_t keylen; -} hlptopic_t; - -#endif /* HELPFILEDEFS */ -#endif +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + + +#ifndef _CMDDEF_H +#define _CMDDEF_H + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif +#include +#include +#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 DT_NAME "medium_type" + +#define DECL_CMD(x) vmscond_t 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; + +typedef vmscond_t (*cmdproc_t) (int argc,char *argv[],int qualc,char *qualv[]); + +/* Command table entry */ + +struct CMDSET { + char *name; + cmdproc_t proc; + int uniq; + qualp_t validquals; + paramp_t params; + char *helpstr; +}; + +/* set, clear: bits specified are cleared, then set in value + * qtype = qualifier's value type. + => value required, - optional, 0 none + * Following macros simplify setting up the tables. + * helpstr: if null, switch not listed in help. If starts with -, + * listed as negatable in help. If null string (""), listed + * as value table. Otherwise, .hlp file lookup key. + */ +#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) +#define PV(val) PROT, ((void *)val) +#define UV(val) UIC, ((void *)val) + +/* Use VOPT(XX()) for optional value */ +#define VOPT(def) -def + +struct qual { + const char *name; + options_t set; + options_t clear; + enum qualtype { opt_optional = -1, NOVAL = 0, /* enum must be signed */ + KEYVAL, KEYCOL, DECVAL, STRVAL, PROT, UIC } qtype; + void *arg; + 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 = 1, OPT = 2, CND = 4, NOLIM = 8 } flags; + enum partype { NONE = 0, FSPEC, LIST, KEYWD, QUALS, STRING } ptype; +#define PA(arg) NULL, (arg) +#define NOPA PA(NULL) + hlpfunc_t *hlpfnc; + void *arg; + char *helpstr; +}; + +/* Default qualifer sets */ + +struct defquals { + struct defquals *next; + int qualc; + char **qualv; + char *data[1]; +}; + +typedef struct defquals defquals_t; +typedef defquals_t *defqualsp_t; + +extern const char *qstyle_s; +#define qstyle_c (qstyle_s[0]) + +extern int verify_cmd; +extern int error_exit; + +vmscond_t parselist( int *nret, char ***items, size_t min, char *arg ); +vmscond_t checkquals( options_t *result, options_t defval, + qualp_t qualset, int qualc,char **qualv ); + +int keycomp(const char *param, const char *keywrd); +vmscond_t confirm_cmd( vmscond_t status, ... ); + +vmscond_t sethelpfile( char *filename, char *env, char *pname ); +vmscond_t showhelpfile( void ); + +vmscond_t helptopic( options_t options, char *topic, ... ); +#define HLP_OPTIONAL 0x00000001 +#define HLP_WRAP 0x00000002 +#define HLP_RELOAD 0x00000004 +#define HLP_ABBREV 0x00000008 + +typedef struct pause { + size_t interval; + size_t lineno; + vmscond_t prompt; +} pause_t; + +vmscond_t put_c( int c, FILE *outf, pause_t *pause ); +vmscond_t put_str( const char *buf, FILE *outf, pause_t *pause ); +vmscond_t put_strn( const char *buf, size_t len, FILE *outf, pause_t *pause ); +void termchar( int *width, int *height ); + +vmscond_t prvmstime(FILE *of, VMSTIME vtime, const char *sfx); +void pwrap( FILE *of, int *pos, const char *fmt, ... ); + +#ifdef HELPFILEDEFS +/* File and index structures private to helpcmd and makehelp. */ + +/* Helpfile magic cookie, version & header */ + +#define HLP_MAGIC "ODS2HLP" +#define HLP_VERSION 1 + +typedef struct hlphdr { + char magic[ sizeof(HLP_MAGIC) ]; + uint32_t version; + uint32_t psize, ssize, tsize; + time_t ddate; + size_t size; +} hlphdr_t; + +/* In-memory pointer / file offsets */ + +typedef union ptr { + void *ptr; + ptrdiff_t ofs; +} ptr_t; + +/* Help tree topic nodes (including root) */ + +typedef struct hlproot { + ptr_t topics; /*struct hlptopic * */ + size_t ntopics; +} hlproot_t; + +/* Help topic data under each node */ + +typedef struct hlptopic { + ptr_t key; /* char * */ + ptr_t text; /* char * */ + hlproot_t subtopics; + uint32_t keylen; +} hlptopic_t; + +#endif /* HELPFILEDEFS */ +#endif diff --git a/extracters/ods2/compat.c b/extracters/ods2/compat.c index 5fb2627..5431b3e 100644 --- a/extracters/ods2/compat.c +++ b/extracters/ods2/compat.c @@ -1,450 +1,450 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -/* 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, 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 -#include "compat.h" -#include "descrip.h" -#include "stsdef.h" - -#ifdef VMS -#include -#else -#ifdef _WIN32 -#include -#include /* PathSearchAndQualify() */ -#else -#include -#include -#ifndef USER_FROM_ENV -#include -#endif -#endif -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1900 - -/******************************************************************* c99_vsnprintf() */ - -int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { - int count = -1; - - if (size != 0) - count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -/******************************************************************* c99_snprintf() */ - -int c99_snprintf(char *outBuf, size_t size, const char *format, ...) { - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(outBuf, size, format, ap); - va_end(ap); - - return count; -} -#endif - -#ifdef _MSC_VER - -/******************************************************************* openf() */ - -FILE *openf( const char *filename, const char *mode ) { - errno_t err; - FILE *fd = NULL; - - err = fopen_s( &fd, filename, mode ); - if( err == 0 ) { - return fd; - } - return NULL; -} -#endif - -#ifdef _WIN32 - -/******************************************************************* ods2_strerror() */ - -const char *ods2_strerror( int errn ) { - static char buf[256]; - - if( strerror_s( buf, sizeof( buf ), errn ) != 0 ) - (void) snprintf( buf, sizeof( buf ), "Untranslatable error %u", errn ); - - return buf; -} - -/******************************************************************* w32_errstr() */ - -TCHAR *w32_errstr( DWORD eno, ... ) { - va_list ap; - TCHAR *msg = NULL; - - if( eno == NO_ERROR ) - eno = GetLastError(); - va_start(ap, eno); - - if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, NULL, eno, 0, - (LPSTR)&msg, 1, &ap ) == 0 ) { - msg = (TCHAR *)malloc( 64 ); - if( msg == NULL ) { - printf( "Out of memory reporting Windows error(%u\n", eno ); - } else - (void) snprintf( msg, 64, "(%u)", eno ); - } - va_end(ap); - return msg; -} - -/******************************************************************* driveFromLetter() */ - -char *driveFromLetter( const char *letter ) { - DWORD rv = ERROR_INSUFFICIENT_BUFFER; - DWORD cs = 16; - TCHAR *bufp = NULL; - - do { - if( rv == ERROR_INSUFFICIENT_BUFFER ) { - TCHAR *newp; - cs *= 2; - newp = (TCHAR *) realloc( bufp, cs ); - if( newp == NULL ) - break; - bufp = newp; - } - rv = QueryDosDevice( letter, bufp, cs ); - if( rv == 0 ) { - rv = GetLastError(); - continue; - } - return bufp; - } while( rv == ERROR_INSUFFICIENT_BUFFER ); - - free( bufp ); - return NULL; -} -#endif - -/******************************************************************* 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, name ); - 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( $SUCCESSFUL(lib$getjpi( &JPI$_USERNAME, 0, 0, 0, &userdsc, 0 )) ) { - 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; -} - -/******************************************************************* homefile () */ - -char *homefile( int dotfile, const char *filename, const char *ext ) { - char * prefix, *delim, *name; - size_t pfxlen, dellen, namlen, extlen; - -#ifdef _WIN32 - char *drive; - - prefix = drive = get_env( "HOMEDRIVE" ); - if( drive != NULL ) { - prefix = get_env( "HOMEPATH" ); - if( prefix != NULL ) { - dellen = strlen( drive ); - pfxlen = strlen( prefix ); - if( (name = malloc( dellen + pfxlen + 1 )) == NULL ) { - free( prefix ); - free( drive ); - return NULL; - } - memcpy( name, drive, dellen ); - memcpy( name+dellen, prefix, pfxlen +1 ); - free( prefix ); - prefix = name; - } - free( drive ); - } - delim = dotfile? "\\.": "\\"; -#elif defined( VMS ) - prefix = get_env( "SYS$LOGIN" ); - delim = dotfile? "_": ""; -#else - prefix = get_env( "HOME" ); - delim = dotfile? "/." : "/"; -#endif - pfxlen = prefix? strlen( prefix ) : 0; - dellen = strlen( delim ); - namlen = strlen( filename ); - extlen = ext? strlen( ext ) : 0; - - if( (name = malloc( pfxlen+dellen+namlen+extlen+1)) == NULL ) - return NULL; - /* N.B. There is no buffer overrun here */ - if( pfxlen ) - memcpy( name, prefix, pfxlen ); - memcpy( name+pfxlen, delim, dellen ); - memcpy( name+pfxlen+dellen, filename, namlen+1 ); - if( ext ) - memcpy( name+pfxlen+dellen+namlen, ext, extlen+1 ); - free( prefix ); - - return name; -} - -/************************************************************* get_realpath() */ - -char *get_realpath( const char *filnam ) { -#ifdef _WIN32 - size_t n; - char *path, resultant_path[MAX_PATH]; - - if ( filnam == NULL || strpbrk( filnam, "*?" ) != NULL || - !PathSearchAndQualify( filnam, /* pcszPath */ - resultant_path, /* pszFullyQualifiedPath */ - /* cchFullyQualifiedPath */ - sizeof( resultant_path ) - ) ) { - return NULL; - } - n = strlen( resultant_path ); - path = (char *) malloc( n + 1 ); - if ( path != NULL ) { - memcpy( path, resultant_path, n + 1 ); - } - return path; -#elif defined VMS -#define MAXPATH 255 - size_t n; - unsigned short resultant_path[1 + ( ( MAXPATH + 1 ) / 2)]; - char *path; - unsigned long context, sts, flags; - struct dsc$descriptor filespec = { - 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL - }; - struct dsc$descriptor resultant_filespec = { - MAXPATH, DSC$K_DTYPE_T, DSC$K_CLASS_VS, (char *) resultant_path - }; - - path = NULL; - if ( filnam != NULL ) { - filespec.dsc$w_length = strlen( filnam ); - filespec.dsc$a_pointer = (char *) filnam; - *resultant_path = 0; - context = 0; - flags = LIB$M_FIL_NOWILD; - sts = lib$find_file( &filespec, &resultant_filespec, &context, - NULL, NULL, &sts, &flags ); - lib$find_file_end( &context ); - if( $SUCCESSFUL(sts) ) { - n = *resultant_path; - path = (char *) malloc( n + 1 ); - if ( path != NULL ) { - memcpy( path, resultant_path + 1, n ); - path[n] = '\0'; - } - } - } - return path; -#elif defined unix || 1 - size_t n; - char *path, resolved_path[PATH_MAX+1]; - - if ( filnam == NULL || realpath( filnam, resolved_path ) == NULL ) { - return NULL; - } - n = strlen( resolved_path ); - path = (char *) malloc( n + 1 ); - if ( path != NULL ) { - memcpy( path, resolved_path, n + 1 ); - } - return path; -#endif -} - -/******************************************************************** Ctime() */ - -char *Ctime( time_t *tval ) { - char *buf; - -#ifdef _WIN32 - if( (buf = malloc( 26 )) == NULL ) return NULL; - if( ctime_s( buf, 26, tval ) != 0 ) { - free( buf ); - return NULL; - } -#else - char *cbuf; - - if( (buf = malloc( 25 )) == NULL ) return NULL; - if( (cbuf = ctime( tval )) == NULL ) { - free( buf ); - return NULL; - } - memcpy( buf, cbuf, 24 ); -#endif - buf[24] = '\0'; - return buf; -} - -/******************************************************************* fgetline() */ -/* Read a line of input - unlimited length - * Removes \n, returns NULL at EOF - * If *buf is NULL, one is allocated of size *bufsize. - * Thereafter, it is reused. Expanded if necessary. - * Caller responsible for free() - */ -char *fgetline( FILE *stream, int keepnl, char **buf, size_t *bufsize ) { - int c; - size_t idx = 0; - - if( *buf == NULL && (*buf = malloc( *bufsize )) == NULL ) { - perror( "malloc" ); - exit(EXIT_FAILURE); - } - - while( (c = getc( stream )) != EOF && c != '\n' ) { - if( idx + (keepnl != 0) +2 > *bufsize ) { /* Now + char + (? \n) + \0 */ - char *nbuf; - *bufsize += 32; - nbuf = (char *) realloc( *buf, *bufsize ); - if( nbuf == NULL ) { - perror( "realloc" ); - exit(EXIT_FAILURE); - } - *buf = nbuf; - } - buf[0][idx++] = c; - } - if( c == '\n' ) { - if( keepnl ) - buf[0][idx++] = '\n'; - } else { - if( c == EOF && idx == 0 ) { - buf[0][0] = '\0'; - return NULL; - } - } - buf[0][idx] = '\0'; - return *buf; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +/* 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, 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 +#include "compat.h" +#include "descrip.h" +#include "stsdef.h" + +#ifdef VMS +#include +#else +#ifdef _WIN32 +#include +#include /* PathSearchAndQualify() */ +#else +#include +#include +#ifndef USER_FROM_ENV +#include +#endif +#endif +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 + +/******************************************************************* c99_vsnprintf() */ + +int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +/******************************************************************* c99_snprintf() */ + +int c99_snprintf(char *outBuf, size_t size, const char *format, ...) { + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} +#endif + +#ifdef _MSC_VER + +/******************************************************************* openf() */ + +FILE *openf( const char *filename, const char *mode ) { + errno_t err; + FILE *fd = NULL; + + err = fopen_s( &fd, filename, mode ); + if( err == 0 ) { + return fd; + } + return NULL; +} +#endif + +#ifdef _WIN32 + +/******************************************************************* ods2_strerror() */ + +const char *ods2_strerror( int errn ) { + static char buf[256]; + + if( strerror_s( buf, sizeof( buf ), errn ) != 0 ) + (void) snprintf( buf, sizeof( buf ), "Untranslatable error %u", errn ); + + return buf; +} + +/******************************************************************* w32_errstr() */ + +TCHAR *w32_errstr( DWORD eno, ... ) { + va_list ap; + TCHAR *msg = NULL; + + if( eno == NO_ERROR ) + eno = GetLastError(); + va_start(ap, eno); + + if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, NULL, eno, 0, + (LPSTR)&msg, 1, &ap ) == 0 ) { + msg = (TCHAR *)malloc( 64 ); + if( msg == NULL ) { + printf( "Out of memory reporting Windows error(%u\n", eno ); + } else + (void) snprintf( msg, 64, "(%u)", eno ); + } + va_end(ap); + return msg; +} + +/******************************************************************* driveFromLetter() */ + +char *driveFromLetter( const char *letter ) { + DWORD rv = ERROR_INSUFFICIENT_BUFFER; + DWORD cs = 16; + TCHAR *bufp = NULL; + + do { + if( rv == ERROR_INSUFFICIENT_BUFFER ) { + TCHAR *newp; + cs *= 2; + newp = (TCHAR *) realloc( bufp, cs ); + if( newp == NULL ) + break; + bufp = newp; + } + rv = QueryDosDevice( letter, bufp, cs ); + if( rv == 0 ) { + rv = GetLastError(); + continue; + } + return bufp; + } while( rv == ERROR_INSUFFICIENT_BUFFER ); + + free( bufp ); + return NULL; +} +#endif + +/******************************************************************* 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, name ); + 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( $SUCCESSFUL(lib$getjpi( &JPI$_USERNAME, 0, 0, 0, &userdsc, 0 )) ) { + 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; +} + +/******************************************************************* homefile () */ + +char *homefile( int dotfile, const char *filename, const char *ext ) { + char * prefix, *delim, *name; + size_t pfxlen, dellen, namlen, extlen; + +#ifdef _WIN32 + char *drive; + + prefix = drive = get_env( "HOMEDRIVE" ); + if( drive != NULL ) { + prefix = get_env( "HOMEPATH" ); + if( prefix != NULL ) { + dellen = strlen( drive ); + pfxlen = strlen( prefix ); + if( (name = malloc( dellen + pfxlen + 1 )) == NULL ) { + free( prefix ); + free( drive ); + return NULL; + } + memcpy( name, drive, dellen ); + memcpy( name+dellen, prefix, pfxlen +1 ); + free( prefix ); + prefix = name; + } + free( drive ); + } + delim = dotfile? "\\.": "\\"; +#elif defined( VMS ) + prefix = get_env( "SYS$LOGIN" ); + delim = dotfile? "_": ""; +#else + prefix = get_env( "HOME" ); + delim = dotfile? "/." : "/"; +#endif + pfxlen = prefix? strlen( prefix ) : 0; + dellen = strlen( delim ); + namlen = strlen( filename ); + extlen = ext? strlen( ext ) : 0; + + if( (name = malloc( pfxlen+dellen+namlen+extlen+1)) == NULL ) + return NULL; + /* N.B. There is no buffer overrun here */ + if( pfxlen ) + memcpy( name, prefix, pfxlen ); + memcpy( name+pfxlen, delim, dellen ); + memcpy( name+pfxlen+dellen, filename, namlen+1 ); + if( ext ) + memcpy( name+pfxlen+dellen+namlen, ext, extlen+1 ); + free( prefix ); + + return name; +} + +/************************************************************* get_realpath() */ + +char *get_realpath( const char *filnam ) { +#ifdef _WIN32 + size_t n; + char *path, resultant_path[MAX_PATH]; + + if ( filnam == NULL || strpbrk( filnam, "*?" ) != NULL || + !PathSearchAndQualify( filnam, /* pcszPath */ + resultant_path, /* pszFullyQualifiedPath */ + /* cchFullyQualifiedPath */ + sizeof( resultant_path ) + ) ) { + return NULL; + } + n = strlen( resultant_path ); + path = (char *) malloc( n + 1 ); + if ( path != NULL ) { + memcpy( path, resultant_path, n + 1 ); + } + return path; +#elif defined VMS +#define MAXPATH 255 + size_t n; + unsigned short resultant_path[1 + ( ( MAXPATH + 1 ) / 2)]; + char *path; + unsigned long context, sts, flags; + struct dsc$descriptor filespec = { + 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL + }; + struct dsc$descriptor resultant_filespec = { + MAXPATH, DSC$K_DTYPE_T, DSC$K_CLASS_VS, (char *) resultant_path + }; + + path = NULL; + if ( filnam != NULL ) { + filespec.dsc$w_length = strlen( filnam ); + filespec.dsc$a_pointer = (char *) filnam; + *resultant_path = 0; + context = 0; + flags = LIB$M_FIL_NOWILD; + sts = lib$find_file( &filespec, &resultant_filespec, &context, + NULL, NULL, &sts, &flags ); + lib$find_file_end( &context ); + if( $SUCCESSFUL(sts) ) { + n = *resultant_path; + path = (char *) malloc( n + 1 ); + if ( path != NULL ) { + memcpy( path, resultant_path + 1, n ); + path[n] = '\0'; + } + } + } + return path; +#elif defined unix || 1 + size_t n; + char *path, resolved_path[PATH_MAX+1]; + + if ( filnam == NULL || realpath( filnam, resolved_path ) == NULL ) { + return NULL; + } + n = strlen( resolved_path ); + path = (char *) malloc( n + 1 ); + if ( path != NULL ) { + memcpy( path, resolved_path, n + 1 ); + } + return path; +#endif +} + +/******************************************************************** Ctime() */ + +char *Ctime( time_t *tval ) { + char *buf; + +#ifdef _WIN32 + if( (buf = malloc( 26 )) == NULL ) return NULL; + if( ctime_s( buf, 26, tval ) != 0 ) { + free( buf ); + return NULL; + } +#else + char *cbuf; + + if( (buf = malloc( 25 )) == NULL ) return NULL; + if( (cbuf = ctime( tval )) == NULL ) { + free( buf ); + return NULL; + } + memcpy( buf, cbuf, 24 ); +#endif + buf[24] = '\0'; + return buf; +} + +/******************************************************************* fgetline() */ +/* Read a line of input - unlimited length + * Removes \n, returns NULL at EOF + * If *buf is NULL, one is allocated of size *bufsize. + * Thereafter, it is reused. Expanded if necessary. + * Caller responsible for free() + */ +char *fgetline( FILE *stream, int keepnl, char **buf, size_t *bufsize ) { + int c; + size_t idx = 0; + + if( *buf == NULL && (*buf = malloc( *bufsize )) == NULL ) { + perror( "malloc" ); + exit(EXIT_FAILURE); + } + + while( (c = getc( stream )) != EOF && c != '\n' ) { + if( idx + (keepnl != 0) +2 > *bufsize ) { /* Now + char + (? \n) + \0 */ + char *nbuf; + *bufsize += 32; + nbuf = (char *) realloc( *buf, *bufsize ); + if( nbuf == NULL ) { + perror( "realloc" ); + exit(EXIT_FAILURE); + } + *buf = nbuf; + } + buf[0][idx++] = c; + } + if( c == '\n' ) { + if( keepnl ) + buf[0][idx++] = '\n'; + } else { + if( c == EOF && idx == 0 ) { + buf[0][0] = '\0'; + return NULL; + } + } + buf[0][idx] = '\0'; + return *buf; +} diff --git a/extracters/ods2/compat.h b/extracters/ods2/compat.h index 6958d8d..22a77c5 100644 --- a/extracters/ods2/compat.h +++ b/extracters/ods2/compat.h @@ -1,91 +1,91 @@ -/* 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 contributions of the original author and - * subsequent contributors. - */ - -#ifndef COMPAT_H -#define COMPAT_H - -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1900 - -#define snprintf c99_snprintf -#define vsnprintf c99_vsnprintf - -#include -int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap); -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 -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#undef getcwd -#define getcwd(_buf,_siz) _getcwd((_buf), (int)(_siz)) -#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,"") -#define Unlink _unlink -#define Fileno(x) _fileno(x) -#include -#include -#define Chmod _chmod -#ifndef S_IREAD -#define S_IREAD _S_IREAD -#define S_IWRITE _S_IWRITE -#endif - -#undef strerror -#define strerror(n) ods2_strerror(n) -const char *ods2_strerror( errno_t errn ); - -TCHAR *w32_errstr( DWORD eno, ... ); -char *driveFromLetter( const char *letter ); - -#else /* Not WIN32 */ -#include -#define Unlink unlink -#define Fileno(x) fileno(x) -#define Chmod chmod -#endif - -char *get_username( void ); -char *get_env( const char *name ); -char *homefile( int dotfile, const char *filename, const char *ext ); -char *get_realpath( const char *filnam ); -char *Ctime( time_t *tval ); -char *fgetline( FILE *stream, int keepnl, char **buf, size_t *bufsize ); - -#define UNUSED(x) (void)(x) - -#endif +/* 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 contributions of the original author and + * subsequent contributors. + */ + +#ifndef COMPAT_H +#define COMPAT_H + +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1900 + +#define snprintf c99_snprintf +#define vsnprintf c99_vsnprintf + +#include +int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap); +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 +#endif + +#ifdef _WIN32 +#include +#include +#include +#include +#undef getcwd +#define getcwd(_buf,_siz) _getcwd((_buf), (int)(_siz)) +#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,"") +#define Unlink _unlink +#define Fileno(x) _fileno(x) +#include +#include +#define Chmod _chmod +#ifndef S_IREAD +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE +#endif + +#undef strerror +#define strerror(n) ods2_strerror(n) +const char *ods2_strerror( errno_t errn ); + +TCHAR *w32_errstr( DWORD eno, ... ); +char *driveFromLetter( const char *letter ); + +#else /* Not WIN32 */ +#include +#define Unlink unlink +#define Fileno(x) fileno(x) +#define Chmod chmod +#endif + +char *get_username( void ); +char *get_env( const char *name ); +char *homefile( int dotfile, const char *filename, const char *ext ); +char *get_realpath( const char *filnam ); +char *Ctime( time_t *tval ); +char *fgetline( FILE *stream, int keepnl, char **buf, size_t *bufsize ); + +#define UNUSED(x) (void)(x) + +#endif diff --git a/extracters/ods2/copycmd.c b/extracters/ods2/copycmd.c index 193b193..6667b12 100644 --- a/extracters/ods2/copycmd.c +++ b/extracters/ods2/copycmd.c @@ -1,1483 +1,1483 @@ - -/* This is 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#if !defined( DEBUG ) && defined( DEBUG_COPYCMD ) -#define DEBUG DEBUG_COPYCMD -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include -#include - -#ifdef _WIN32 -#include -#include "winfile.h" -#define basename winbasename -#undef fstat -#define fstat _fstat -#elif defined(VMS) -#include "vmsfile.h" -#define basename vmsbasename -#else -#include -#include -#include -#endif - -#ifndef GLOB_BRACE -#define GLOB_BRACE 0 -#endif -#ifndef GLOB_TILDE_CHECK -#define GLOB_TILDE_CHECK 0 -#endif - -#include "cmddef.h" - -static vmscond_t copy_from( options_t options, int argc, char **argv ); -static vmscond_t copy_1_from( options_t options, struct FAB *fab, - const char *to, size_t pause ); -static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ); - -static vmscond_t copy_to( options_t options, int argc, char **argv ); -static vmscond_t copy_1_to( options_t options, char *from, char *to ); - -char *output_file_name( const char *ofname, const char *ifname, int *flags ); -#define OF_WILD 1 - -static int xpand( char **buf, size_t size[2], size_t add ); - -/******************************************************************* dotype() */ - -/* Display a file on the terminal */ - -#define type_page OPT_SHARED_20 -#define type_one OPT_SHARED_19 -#define copy_vfc OPT_SHARED_18 - -static uint32_t height; - -qual_t typequals[] = { {"page", type_page, 0, VOPT(DV(&height)), - "-commands type qual_page"}, - {"nopage", 0, type_page, NV, NULL}, - {"onefile", type_one, 0, NV, NULL}, - {"vfc_header", copy_vfc, 0, NV, - "-commands type qual_vfc"}, - {"novfc_header", 0, copy_vfc, NV, NULL}, - - {NULL, 0, 0, NV, NULL } -}; - -param_t typepars[] = { {"filespec", REQ | NOLIM, FSPEC, NOPA, "commands type filespec"}, - - { NULL, 0, 0, NOPA, NULL } -}; - -DECL_CMD(type) { - vmscond_t sts; - int theight; - options_t options; - struct FAB fab = cc$rms_fab; - struct NAM nam = cc$rms_nam; - char esa[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; - - (void) termchar( NULL, &theight ); - height = theight-1; - - if( $FAILS(sts = checkquals( &options, type_page, typequals, qualc, qualv )) ) { - return sts; - } - if( !(options & type_page) ) { - height = 0; - } - - --argc; - ++argv; - while( $SUCCESSFUL(sts) && argc-- ) { - nam.nam$l_esa = esa; - nam.nam$b_ess = NAM$C_MAXRSS; - - fab.fab$l_fna = argv++[0]; - fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); - - if( options & type_one ) { /* RMS test/debug: implicit parse/search */ - if( $FAILS(sts = sys_open(&fab)) ) { - nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; - printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); - sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); - break; - } - sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); - sys_close(&fab); - continue; - } - fab.fab$l_nam = &nam; - - if( $FAILS(sts = sys_parse(&fab)) ) { - fab.fab$l_fna[fab.fab$b_fns] = '\0'; - printmsg( TYPE_PARSEFAIL, 0, fab.fab$l_fna ); - sts = printmsg( sts, MSG_CONTINUE, TYPE_PARSEFAIL ); - break; - } - - nam.nam$l_rsa = rsa; - nam.nam$b_rss = NAM$C_MAXRSS; - fab.fab$l_fop = 0; - - while( $SUCCESSFUL(sts) ) { - if( $FAILS(sts = sys_search( &fab )) ) { - if( $MATCHCOND(sts, RMS$_NMF) ) - sts = SS$_NORMAL; - nam.nam$l_esa[nam.nam$b_esl] = '\0'; - printmsg( TYPE_SEARCHFAIL, 0, nam.nam$l_esa ); - sts = printmsg( sts, MSG_CONTINUE, TYPE_SEARCHFAIL ); - break; - } - fab.fab$b_fac = FAB$M_GET; - - if( nam.nam$l_fnb & NAM$M_WILDCARD ) - printf( "\n%.*s\n\n", nam.nam$b_rsl, rsa ); - - if( $FAILS(sts = sys_open(&fab)) ) { - nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; - printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); - sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); - break; - } - sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); - - sys_close(&fab); - } - } - printf( "\n" ); - - fab.fab$b_fns = - nam.nam$b_ess = - fab.fab$b_dns = 0; - nam.nam$l_rlf = NULL; - nam.nam$b_nop = NAM$M_SYNCHK; - (void) sys_parse( &fab ); /* Release context */ - - if( $MATCHCOND( sts, RMS$_EOF ) ) - sts = SS$_NORMAL | STS$M_INHIB_MSG; - - return sts; -} - -/******************************************************************* docopy() */ - -/* Copy files to and from Files-11 volume */ - -#define copy_binary OPT_GENERIC_1 -#define copy_log OPT_GENERIC_2 -#define copy_tof11 OPT_GENERIC_3 -#define copy_confirm OPT_GENERIC_4 -#define copy_override OPT_GENERIC_5 - -#define copy_owner OPT_GENERIC_6 -#define copy_protect OPT_GENERIC_7 -#define copy_volume OPT_GENERIC_8 -#define copy_verlim OPT_GENERIC_9 - -#define copy_recfmt (OPT_GENERIC_12|OPT_GENERIC_11|OPT_GENERIC_10) -#define copy_v_recfmt (OPT_V_GENERIC + 9) - -/* GENERIC option space is full */ - -#define copy_b4 (OPT_SHARED_2|OPT_SHARED_1) -#define copy_v_b4 0 - -#define copy_after (OPT_SHARED_4|OPT_SHARED_3) -#define copy_v_after 2 - -#define copy_prn_none 0 -#define copy_prn_nl 1 -#define copy_prn_c0 2 -#define copy_prn_vfu 3 - -#define copy_prn OPT_SHARED_5 -#define copy_ftn OPT_SHARED_6 -#define copy_cr OPT_SHARED_7 -#define copy_span OPT_SHARED_8 -#define copy_append OPT_SHARED_9 - -/* Type uses OPT_SHARED from the high end */ - -static uint32_t b4n, aftn; -static uint32_t vfclen; -static uint32_t reclen; -static uint32_t verlimit; -#if 0 -static uint32_t volume; -#endif -static uint32_t protection; -static uint32_t ownuic; - -/* Print CC */ -static qual_t -prnb4[] = { {"none", copy_prn_none << copy_v_b4, - copy_b4, NV, "commands copy qual_cc print b4 none"}, - {"nl", copy_prn_nl << copy_v_b4, - copy_b4, DV(&b4n), "commands copy qual_cc print b4 nl"}, - {"ctl", copy_prn_c0, copy_b4, DV(&b4n), "commands copy qual_cc print b4 ctl"}, - {"vfu", copy_prn_vfu << copy_v_b4, - copy_b4, DV(&b4n), "commands copy qual_cc print b4 vfu"}, - - {NULL, 0, 0, NV, NULL} -}; -static qual_t /* Same keywords as prnb4. Use same help. */ -prnafter[] = { {"none", copy_prn_none << copy_v_after, - copy_after, NV, "commands copy qual_cc print b4 none"}, - {"nl", copy_prn_nl << copy_v_after, - copy_after, DV(&aftn), "commands copy qual_cc print b4 nl"}, - {"ctl", copy_prn_c0, copy_b4, DV(&aftn), "commands copy qual_cc print b4 ctl"}, - {"vfu", copy_prn_vfu << copy_v_after, - copy_after, DV(&aftn), "commands copy qual_cc print b4 vfu"}, - - {NULL, 0, 0, NV, NULL} -}; - -static qual_t -prnkwds[] = { {"before", 0, copy_b4, KV(prnb4), "commands copy qual_cc print b4"}, - {"after", 0, copy_after, KV(prnafter), "commands copy qual_cc print after"}, - - {NULL, OPT_NOSORT, 0, NV, NULL} -}; - -/* Carriage control record attributes */ -static qual_t -cckwds[] = { {"fortran", copy_ftn, copy_cr|copy_prn, NV, - "commands copy qual_cc fortran"}, - {"carriage_return", copy_cr, copy_ftn|copy_prn, NV, - "commands copy qual_cc cr"}, - {"print", copy_prn|(FAB$C_VFC<qualc, copy_defopt->qualv )) ) { - return sts; - } - } - else - options = copy_defaults; - - if( $FAILS(sts = checkquals( &options, options, copyquals, qualc, qualv )) ) { - return sts; - } - - if( options & copy_tof11 ) - return copy_to( options, argc, argv ); - - return copy_from( options, argc, argv ); - -} - -/******************************************************************* copy_from() */ - -static vmscond_t copy_from( options_t options, int argc, char **argv ) { - vmscond_t sts; - struct NAM nam; - struct FAB fab; - char res[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; - int i, filecount = 0; - vmscond_t confirm = ODS2_CONFIRM_ALL; - - if( options & copy_confirm ) - confirm = COPY_CONFIRM; - - sts = SS$_NORMAL; - - nam = cc$rms_nam; - fab = cc$rms_fab; - - for( i = 1; i < argc -1; i++ ) { - 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 = (uint8_t)strlen(fab.fab$l_fna); - - if( $FAILS(sts = sys_parse(&fab)) ) { - printmsg( COPY_PARSEFAIL, 0, fab.fab$l_fna ); - sts = printmsg( sts, MSG_CONTINUE, COPY_PARSEFAIL ); - break; - } - res[nam.nam$b_esl] = '\0'; - - nam.nam$l_rsa = rsa; - nam.nam$b_rss = NAM$C_MAXRSS; - fab.fab$l_fop = 0; - - while( $SUCCESSFUL(sts) ) { - char name[NAM$C_MAXRSS + 1]; - char *out, *inp; - int dot = FALSE; - uint32_t directory = 0; - uint16_t retlen; - struct XABPRO pro; - struct XABDAT dat; - struct XABITM itm; - struct item_list xitems[] = { - { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, - { 0, 0, NULL, 0 } - }; - - if( $FAILS(sts = sys_search( &fab )) ) { - if( $MATCHCOND(sts, RMS$_NMF) ) { - sts = SS$_NORMAL; - break; - } - nam.nam$l_esa[nam.nam$b_esl] = '\0'; - printmsg( COPY_SEARCHFAIL, 0, nam.nam$l_esa ); - sts = printmsg( sts, MSG_CONTINUE, COPY_SEARCHFAIL ); - break; - } - rsa[nam.nam$b_rsl] = '\0'; - - pro = cc$rms_xabpro; - dat = cc$rms_xabdat; - itm = cc$rms_xabitm; - - dat.xab$l_nxt = &pro; - itm.xab$l_nxt = &dat; - xitems[0].buffer = &directory; - xitems[0].retlen = &retlen; - itm.xab$b_mode = XAB$K_SENSEMODE; - itm.xab$l_itemlist = xitems; - fab.fab$l_xab = &itm; - fab.fab$b_fac = FAB$M_GET; - - sts = sys_open(&fab); - fab.fab$l_xab = NULL; - rsa[nam.nam$b_rsl] = '\0'; - - if( $FAILED(sts) ) { - int err; - err = errno; - printmsg( COPY_OPENIN, 0, rsa ); - sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); - if( err ) - printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENIN, - strerror( err ) ); - break; - } - - if( directory ) { - if( !(options & copy_override) ) { - printmsg( COPY_DIRNOTCOPIED, 0, rsa ); - sys_close( &fab ); - continue; - } - } - - out = name; - inp = argv[2]; - - 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 { - size_t length; - - length = nam.nam$b_name; - - if (*inp == '\0') - length += nam.nam$b_type; - memcpy( out, nam.nam$l_name, length ); - out += length; - } - continue; - } - if (*inp == '.') { - dot = TRUE; - } else { - if (strchr(":]\\/", *inp)) - dot = FALSE; - } - *out++ = *inp++; - } - *out++ = '\0'; - - if( $MATCHCOND( confirm, COPY_CONFIRM) ) - confirm = confirm_cmd( confirm, rsa, name ); - - if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { - if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { - confirm = COPY_CONFIRM; - } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { - confirm = COPY_CONFIRM; - sys_close( &fab ); - continue; - } else { /* ODS2_CONFIRM_QUIT */ - sys_close( &fab ); - sts = confirm; - break; - } - } - - fab.fab$l_xab = &itm; - if( $SUCCESSFUL(sts = copy_1_from( options, &fab, name, 0 )) ) - filecount++; - fab.fab$l_xab = NULL; - - sys_close(&fab); - } /* search */ - if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || - $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) - break; - } /* arg */ - - if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { - if (filecount > 0) { - if( options & copy_log ) - sts = printmsg( COPY_COPIED, 0, filecount ); - } else { - sts = printmsg( COPY_NOFILES, 0 ); - } - } - return sts; -} - -/******************************************************************* copy_1_from() */ - -static vmscond_t copy_1_from( options_t options, struct FAB *fab, - const char *to, size_t dopause ) { - vmscond_t sts; - uint32_t records = 0; - int stream = 0, var = 0; - FILE *tof; - char *rec; - pause_t *pause = NULL, pausectl; - struct RAB rab = cc$rms_rab; - - if( dopause ) { - pausectl.interval = dopause; - pausectl.lineno = 0; - pausectl.prompt = TYPE_CONTINUE; - pause = &pausectl; - } - - rab.rab$l_fab = fab; - - if( $FAILS(sts = sys_connect(&rab)) ) { - printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); - sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); - return sts; - } - - if( (rec = malloc( MAXREC + 2 )) == NULL ) { - sys_disconnect( &rab ); - printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); - sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, COPY_OPENIN ); - return sts; - } - - rab.rab$l_ubf = rec; - rab.rab$w_usz = MAXREC; - rab.rab$l_rhb = NULL; - - if( options & copy_binary ) { - fab->fab$b_rfm = FAB$C_UDF; - fab->fab$b_rat = 0; - } else { - switch( fab->fab$b_rfm ) { - case FAB$C_STMLF: - stream = '\n'; - break; - case FAB$C_STMCR: - stream = '\r'; - break; - case FAB$C_STM: - stream = EOF; - break; - case FAB$C_VFC: - var = 1; - if( fab-> fab$b_fsz != 0 ) { - ++var; - rab.rab$l_rhb = malloc( fab->fab$b_fsz ); - if( rab.rab$l_rhb == NULL ) { - free( rec ); - sys_disconnect( &rab ); - printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); - sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); - return sts; - } - } - break; - case FAB$C_VAR: - var = 1; - break; - case FAB$C_FIX: - break; - } - } - - if( to == NULL ) - tof = stdout; - else { - if( (stream || - (fab->fab$b_rat & (FAB$M_CR | FAB$M_PRN | FAB$M_FTN))) && - !(options & copy_binary) ) { - tof = openf( to, (options & copy_append)? "a": "w" ); - } else { - tof = openf( to, (options & copy_append)? "ab": "wb" ); - } - } - if (tof == NULL) { - int err; - - err = errno; - free( rec ); - free( rab.rab$l_rhb ); - sys_disconnect( &rab ); - printmsg( COPY_OPENOUT, 0, to ); - return printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENOUT, - strerror( err ) ); - } - - while( $SUCCESSFUL(sts) ) { - char *rp; - unsigned rsz; - - if( ferror( tof ) ) { - int err; - - err = errno; - printmsg( COPY_WRITEERR, 0, (to == NULL)? "tty": to ); - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_WRITEERR, - strerror(err) ); - break; - } - if( $FAILS(sts = sys_get( &rab )) ) { - if( $MATCHCOND(sts, RMS$_EOF) ) - sts |= STS$M_INHIB_MSG; - else { - printmsg( COPY_READERR, 0, fab->fab$l_fna ); - sts = printmsg( sts, MSG_CONTINUE, COPY_READERR, rab.rab$l_stv); - } - break; - } - rp = rec; - rsz = rab.rab$w_rsz; - - if( (options & copy_binary) || fab->fab$b_rfm == FAB$C_UDF ) { - if( rsz != 0 ) { - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - if( fab->fab$b_rfm == FAB$C_UDF ) - records += rsz; - else - records++; - } - continue; - } - - if( stream ) { - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - - if( fab->fab$b_rat & (FAB$M_CR|FAB$M_FTN) ) { - if( stream == EOF ) { - if( $FAILS(sts = put_strn( "\r\n", 2, tof, pause )) ) - break; - } else { - if( $FAILS(sts = put_c( stream, tof, pause )) ) - break; - } - } - ++records; - continue; - } - - if( fab->fab$b_rat & FAB$M_FTN ) { - --rsz; - switch( *rp++ ) { /* AA-D034E-TE Table 8-5 */ - case '+': /* Overprint current line and return to left margin */ - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - break; - case ' ': /* Single space: output to beginning of next line */ - default: - if( $FAILS(sts = put_c( '\n', tof, pause )) ) - break; - if( $FAILS(sts = put_strn(rp, rsz, tof, pause )) ) - break; - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - break; - case '0': /* Double space: skip a line before starting output */ - if( $FAILS(sts = put_strn( "\n\n", 2, tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - break; - case '1': /* Paging: start output at top of a new page */ - if( $FAILS(sts = put_c( '\f', tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - break; - case '$': /* Prompting: Start a beginning of next line and suppress \r */ - if( $FAILS(sts = put_c( '\n', tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - break; - case '\0': /* Overprinting with no advance - no \r at end */ - if( $FAILS(sts = put_c( '\r', tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - break; - } - if( $FAILED(sts) ) - break; - ++records; - continue; - } - - if( (fab->fab$b_rat & FAB$M_CR) && $FAILS(sts = put_c( '\n', tof, pause )) ) - break; - - if( var == 2 ) { - if( (fab->fab$b_rat & FAB$M_PRN) && fab->fab$b_fsz == 2 ) { - if( $FAILS(sts = put_prn( rab.rab$l_rhb[0], tof, pause )) ) - break; - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - if( $FAILS(sts = put_prn( rab.rab$l_rhb[1], tof, pause )) ) - break; - ++records; - continue; - } - - if( (options & copy_vfc) && - $FAILS(sts = put_strn( rab.rab$l_rhb, - fab->fab$b_fsz, tof, pause )) ) - break; - } - - if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) - break; - - if( fab->fab$b_rat & FAB$M_CR ) - fputc( '\r', tof ); - - ++records; - } - - sys_disconnect( &rab ); - - if( var == 2 ) { - free( rab.rab$l_rhb ); - rab.rab$l_rhb = NULL; - } - - free( rec ); - rec = NULL; - - if( to == NULL ) { /* stdout */ - if( $FAILED(sts) ) - sts = printmsg( sts, 0, rab.rab$l_stv ); - return sts; - } - if( fclose( tof ) ) { - int err; - - err = errno; - printmsg( COPY_CLOSEOUT, 0, (to == NULL)? "tty": to ); - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_CLOSEOUT, strerror( err ) ); - return sts; - } - - do { - struct XABDAT *dat; - time_t posixtime; - unsigned short timvec[7]; - char *tz; - struct tm tm; -#ifdef _WIN32 - struct _utimbuf tv; -#else - struct timeval tv[2]; -#endif - - for( dat = (struct XABDAT *)fab->fab$l_xab; - dat != NULL && dat->xab$b_cod != XAB$C_DAT; dat = dat->xab$l_nxt ) - ; - - if( dat == NULL ) - break; - - if( $FAILS(sys_numtim( timvec, dat->xab$q_rdt )) ) - break; - - 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( to, &tv ); -#else - tv[0].tv_sec = posixtime; - tv[0].tv_usec = timvec[6] * 10000; - tv[1] = tv[0]; /* Set mtime to atime */ - (void) utimes( to, tv ); -#endif - } - if( tz != NULL ) - setenv( "TZ", tz, 1 ); - else - unsetenv( "TZ" ); - tzset(); - free( tz ); - - } while( 0 ); - -#ifndef _WIN32 - do { - struct XABPRO *pro; - mode_t mode; - uint16_t prot; - - for( pro = (struct XABPRO *)fab->fab$l_xab; - pro != NULL && pro->xab$b_cod != XAB$C_PRO; pro = pro->xab$l_nxt ) - ; - - if( pro == NULL ) - break; - - mode = (S_IRUSR|S_IWUSR|S_IXUSR| - S_IRGRP|S_IWGRP|S_IXGRP| - S_IROTH|S_IWOTH|S_IXOTH); - - prot = pro->xab$w_pro; - - if( prot & ((xab$m_noread << xab$v_system) | - (xab$m_noread << xab$v_owner)) ) - mode &= ~S_IRUSR; - if( prot & (((xab$m_nodel|xab$m_nowrite) << xab$v_system) | - ((xab$m_nodel|xab$m_nowrite) << xab$v_owner)) ) - mode &= ~S_IWUSR; - if( prot & ( (xab$m_noexe << xab$v_system) | - (xab$m_noexe << xab$v_owner) ) ) - mode &= ~S_IXUSR; - if( prot & (xab$m_noread << xab$v_group) ) - mode &= ~S_IRGRP; - if( prot & (xab$m_nowrite << xab$v_group) ) - mode &= ~S_IWGRP; - if( prot & (xab$m_noexe << xab$v_group) ) - mode &= ~S_IXGRP; - if( prot & (xab$m_noread << xab$v_world) ) - mode &= ~S_IROTH; - if( prot & (xab$m_nowrite << xab$v_world) ) - mode &= ~S_IWOTH; - if( prot & (xab$m_noexe << xab$v_world) ) - mode &= ~S_IXOTH; - if( chmod( to, mode ) != 0 ) { - int err; - - err = errno; - printmsg( COPY_PROTERR, 0, to ); - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_PROTERR, - strerror( err ) ); - } - } while( 0 ); -#endif - - if( !$MATCHCOND(sts, RMS$_EOF) ) - return sts; - - if( options & copy_log ) { - sts = (fab->fab$b_rfm == FAB$C_UDF)? COPY_COPIEDU: COPY_COPIEDR; - - sts = printmsg( sts, 0, - fab->fab$l_nam->nam$l_rsa, - (to == NULL)? "tty": to, - records ); - } - return SS$_NORMAL; -} - -/******************************************************************* put_prn() */ -static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ) { - vmscond_t sts; - size_t n; - /* 7 6 5 4 */ - if( !(ch & 0x80) ) { /* 0 x x x */ - n = ch & 0x7F; - while( n-- ) - if( $FAILS(sts = put_c( '\n', tof, pause )) ) - return sts; - return SS$_NORMAL; - } /* 1 x x x */ - if( !(ch & 0x40) ) { /* 1 0 x x */ - if( !(ch & 0x20) ) /* 1 0 0 x */ - return put_c( ch & 0x1f, tof, pause ); /* C0 */ - /* 1 0 1 x */ - return put_c( (ch & 0x1f) | 0x80, tof, pause ); /* C1/reserved */ - } /* 1 1 x x */ - if( !(ch & 0x30) ) { /* 1 1 0 0 */ - static const char vfuch[16] = { - '\f', '\020', '\021', '\022', /* CH 1 - 4 */ - '\023', '\024', '\v', '\n', /* CH 5 - 8 */ - '\n', '\n', '\n', '\n', /* CH 9 - 12 */ - '\n', '\n', '\n', '\n' }; /* CH 13 - 16 */ - if( tof == stdout ) - return put_c( '\n', tof, pause ); /* type: 1 LF */ - return put_c( vfuch[ch & 0xf], tof, pause ); - /* VFU channel 1+<3:0> */ - } /* 1 1 y y : yy == 01, 10, 11 */ - return put_c( ch, tof, pause ); /* Device-specific/reserved -- Agrave - yumlaut */ -} - -/******************************************************************* copy_to() */ - -static vmscond_t copy_to( options_t options, int argc, char **argv ) { - glob_t globs; - int i, err, gflags = GLOB_MARK | GLOB_BRACE | GLOB_TILDE_CHECK; - size_t n, filecount = 0; - vmscond_t sts; - vmscond_t confirm = ODS2_CONFIRM_ALL; - - memset( &globs, 0, sizeof( globs ) ); - - if( options & copy_confirm ) - confirm = COPY_CONFIRM; - - sts = SS$_NORMAL; - - for( i = 1; i < argc -1; i++ ) { - err = glob( argv[i], gflags, NULL, &globs ); - gflags |= GLOB_APPEND; - switch( err ) { - case 0: - sts = SS$_NORMAL; - break; - case GLOB_NOSPACE: - sts = printmsg( COPY_NOMEM, 0, argv[i] ); - break; - case GLOB_ABORTED: - sts = printmsg( COPY_GLOBABT, 0, argv[i] ); - break; - case GLOB_NOMATCH: - sts = printmsg( COPY_NOMATCH, 0, argv[i] ); - break; - default: - sts = printmsg( COPY_GLOBERR, 0, err, argv[i] ); - break; - } - if( $FAILED(sts) ) { - globfree( &globs ); - return sts; - } - } - for( n = 0; n < globs.gl_pathc; n++ ) { - char *to; - - if( (to = output_file_name( argv[ argc - 1], - globs.gl_pathv[n], NULL )) == NULL ) { - sts = printmsg( COPY_NOMEM, 0, globs.gl_pathv[n] ); - globfree( &globs ); - return sts; - } - - if( $MATCHCOND( confirm, COPY_CONFIRM) ) - confirm = confirm_cmd( confirm, globs.gl_pathv[n], to ); - - if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { - if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { - confirm = COPY_CONFIRM; - } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { - confirm = COPY_CONFIRM; - free( to ); - continue; - } else { /* ODS2_CONFIRM_QUIT */ - sts = confirm; - free( to ); - break; - } - } - sts = copy_1_to( options, globs.gl_pathv[n], to ); - - free( to ); - if( $SUCCESSFUL(sts ) ) - ++filecount; - else if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || - $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) - break; - } /* glob */ - - if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { - if( filecount > 0 ) { - if( options & copy_log ) - sts = printmsg( COPY_COPIED, 0, filecount ); - } else { - sts = printmsg( COPY_NOFILES, 0 ); - } - } - globfree( &globs ); - return sts; -} - -/******************************************************************* copy_1_to() */ -static vmscond_t copy_1_to( options_t options, char *from, char *to ) { - vmscond_t sts; - size_t records = 0, bufsize = 80; - int keepnl = 0; - char *buf = NULL; - FILE *fromf; - struct FAB fab; - struct RAB rab; - struct NAM nam; - struct XABDAT dat; - struct XABPRO pro; - char vfc[2], rsbuf[NAM$C_MAXRSS+1]; -#ifdef _WIN32 - struct _stat statbuf; -#else - struct stat statbuf; -#endif - - fab = cc$rms_fab; - rab = cc$rms_rab; - nam = cc$rms_nam; - dat = cc$rms_xabdat; - pro = cc$rms_xabpro; - - if ( *from == '\0' ) { - return printmsg( COPY_NONAME, 0 ); - } - fromf = openf( from, (options & copy_binary)? "rb": "r" ); - if( fromf == NULL ) { - int err; - - err = errno; - sts = printmsg( COPY_OPENIN, 0, from ); - if( err ) - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, - COPY_OPENIN, strerror(err) ); - return sts; - } - if( fstat( Fileno( fromf ), &statbuf ) != 0 ) - statbuf.st_size = 0; - else { - unsigned short tvec[7]; - -#ifdef _WIN32 - struct tm tmd; - struct tm *tm = &tmd; - (void) gmtime_s( &tmd, &statbuf.st_mtime ); -#else - struct tm *tm; - - tm = gmtime( &statbuf.st_mtime ); -#endif - tvec[0] = tm->tm_year + 1900; - tvec[1] = tm->tm_mon + 1; - tvec[2] = tm->tm_mday; - tvec[3] = tm->tm_hour; - tvec[4] = tm->tm_min; - tvec[5] = tm->tm_sec; - tvec[6] = 0; - - if( $SUCCESSFUL(lib_cvt_vectim( tvec, dat.xab$q_rdt )) ) { -#ifdef _WIN32 - (void) gmtime_s( &tmd, &statbuf.st_ctime ); - tvec[0] = tm->tm_year + 1900; - tvec[1] = tm->tm_mon + 1; - tvec[2] = tm->tm_mday; - tvec[3] = tm->tm_hour; - tvec[4] = tm->tm_min; - tvec[5] = tm->tm_sec; - tvec[6] = 0; - - (void) lib_cvt_vectim( tvec, dat.xab$q_cdt ); -#else /* No local create time */ - memcpy( &dat.xab$q_cdt, dat.xab$q_rdt, sizeof( dat.xab$q_cdt ) ); -#endif - - fab.fab$l_xab = &dat; - } - } - - nam.nam$l_rsa = rsbuf; - nam.nam$b_rss = (uint8_t)(sizeof( rsbuf ) -1); - - if( options & (copy_protect|copy_owner) ) { - pro.xab$l_nxt = fab.fab$l_xab; - fab.fab$l_xab = &pro; - - if( options & copy_protect ) { - pro.xab$w_pro = (uint16_t) - ((pro.xab$w_pro & ~(protection >> 16)) | - (uint16_t)protection); - } - - if( options & copy_owner ) { - pro.xab$l_uic = ownuic; - } - } - - fab.fab$l_nam = &nam; - - 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 0 - if( options & copy_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_STMLF; - } -#endif - - if( !(options & copy_recfmt) ) { - options |= (FAB$C_VAR +1) << copy_v_recfmt; - if( !(options & (copy_prn|copy_ftn|copy_cr)) ) - options |= copy_cr; - } - - fab.fab$b_rfm = ((options & copy_recfmt) >> copy_v_recfmt) -1; - - switch( fab.fab$b_rfm ) { - case FAB$C_STM: - case FAB$C_STMLF: - case FAB$C_STMCR: - fab.fab$b_rat |= FAB$M_CR; - keepnl = 1; - break; - case FAB$C_UDF: - keepnl = 1; - break; - default: - keepnl - 0; - break; - } - - if( !(options & copy_span) ) - fab.fab$b_rat |= FAB$M_BLK; - if( options & copy_ftn ) - fab.fab$b_rat |= FAB$M_FTN; - if( options & copy_cr ) - fab.fab$b_rat |= FAB$M_CR; - if( options & copy_prn ) { - fab.fab$b_rat |= FAB$M_PRN; - fab.fab$b_fsz = 2; - fab.fab$b_rfm = FAB$C_VFC; - - switch( (options & copy_b4) >> copy_v_b4 ) { - case copy_prn_none: - vfc[0] = 0; - break; - case copy_prn_nl: - if( b4n < 1 || b4n > 127 ) - return printmsg( COPY_INVALIDNL, 0 ); - vfc[0] = b4n & 0x7f; - break; - case copy_prn_c0: - if( b4n > 31 ) - return printmsg( COPY_INVALIDC0, 0 ); - vfc[0] = 0x80 | b4n; - break; - case copy_prn_vfu: - if( b4n < 1 || b4n > 16 ) - return printmsg( COPY_INVALIDVFU, 0 ); - vfc[0] = (0xC0 | (b4n -1)) & 0xff; - } - switch( (options & copy_after) >> copy_v_after ) { - case copy_prn_none: - vfc[1] = 0; - break; - case copy_prn_nl: - if( aftn < 1 || aftn > 127 ) - return printmsg( COPY_INVALIDNL, 0 ); - vfc[1] = aftn & 0x7f; - break; - case copy_prn_c0: - if( aftn > 31 ) - return printmsg( COPY_INVALIDC0, 0 ); - vfc[1] = 0x80 | aftn; - break; - case copy_prn_vfu: - if( aftn < 1 || aftn > 16 ) - return printmsg( COPY_INVALIDVFU, 0 ); - vfc[1] = (0xC0 | (aftn -1)) & 0xff; - } - } /* prn */ - - fab.fab$l_alq = (statbuf.st_size + 511) / 512; - fab.fab$w_mrs = (uint16_t)reclen; - - fab.fab$l_fna = to; - fab.fab$b_fns = (uint8_t)strlen( fab.fab$l_fna ); - - do { - if( options & copy_append ) { - sts = sys_open( &fab ); - rab.rab$l_rop = RAB$M_EOF; - } else - sts = sys_create( &fab ); - rsbuf[nam.nam$b_rsl] = '\0'; - if( $FAILED(sts) ) { - fclose( fromf ); - printmsg( COPY_OPENOUT, 0, (nam.nam$b_rsl? rsbuf:to) ); - return printmsg( sts, MSG_CONTINUE, COPY_OPENOUT ); - } - - rab.rab$l_fab = &fab; - rab.rab$b_rac = RAB$C_SEQ; - - if( $FAILS(sts = sys_connect( &rab )) ) { - sts = printmsg( sts, 0 ); - break; - } - - if( fab.fab$b_rfm == FAB$C_VFC ) { - if( options & copy_prn ) - rab.rab$l_rhb = vfc; - } else - options &= ~(copy_prn|copy_vfc); - - if( options & copy_binary ) { - char *buf; - uint16_t len; - - if( (buf = malloc( 512 )) == NULL ) { - sts = printmsg( COPY_NOMEM, 0, from ); - break; - } - rab.rab$l_rbf = buf; - while( (len = (uint16_t)fread( buf, 1, 512, fromf )) > 0 ) { - if( len != 512 ) - memset( buf + len, 0, 512 - len ); - rab.rab$w_rsz = 512; - - if( $FAILS(sts = sys_put( &rab )) ) { - printmsg( COPY_WRITEERR, 0, rsbuf ); - sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); - break; - } - ++records; - } - free( buf ); - buf = NULL; - break; - } - while( (rab.rab$l_rbf = fgetline( fromf, keepnl, &buf, &bufsize )) != NULL ) { - size_t len; - char * rp; - - rp = rab.rab$l_rbf; - len = strlen( rp ); - if( (options & (copy_prn | copy_vfc)) == copy_vfc ) { - if( len < fab.fab$b_fsz ) { - sts = RMS$_RSZ; - break; - } - rab.rab$l_rhb = rp; - rab.rab$l_rbf = rp + fab.fab$b_fsz; - len -= fab.fab$b_fsz; - } - - do { - rab.rab$w_rsz = (uint16_t) (len % (MAXREC + 1)); - sts = sys_put( &rab ); - rab.rab$l_rbf += rab.rab$w_rsz; - len -= rab.rab$w_rsz; - } while( len > 0 && $SUCCESSFUL( sts ) ); - ++records; - if( $FAILED(sts) ) { - printmsg( COPY_WRITEERR, 0, rsbuf ); - sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); - break; - } - } - } while( 0 ); - - if( buf != NULL ) free( buf ); - sys_disconnect( &rab ); - { - vmscond_t clsts; - - if( $FAILS(clsts = sys_close( &fab )) && $SUCCESSFUL(sts) ) { - printmsg( COPY_CLOSEOUT, 0, rsbuf ); - clsts = printmsg( clsts, MSG_CONTINUE, COPY_CLOSEOUT ); - } - if( $SUCCESSFUL(sts) ) - sts = clsts; - } - fclose( fromf ); - - if( options & copy_log ) { - sts = (options & copy_binary)? COPY_COPIEDB: COPY_COPIEDR; - - sts = printmsg( sts, 0, - from, fab.fab$l_nam->nam$l_rsa, - records ); - } - - return sts; -} - -/******************************************************************* output_file_name() */ - -char *output_file_name( const char *ofname, const char *ifname, int *flags ) { - char *ofn, *from, *fname; - const char *p, *q, *e; - size_t len, bufsize[2]; - - if( flags != NULL ) - *flags = 0; - - ofn= NULL; - bufsize[0] = - bufsize[1] = 0; - - len = strlen( ifname ) +1; - if( (from = malloc( len )) == NULL ) { - return NULL; - } - memcpy( from, ifname, len ); -#ifdef _WIN32 - for( fname = from; *fname != '\0'; fname++ ) - if( *fname == '/' ) - *fname = '\\'; -#endif - - fname = basename( from ); - e = strrchr( fname, '.' ); - - p = strrchr( ofname, ']' ); - if( p == NULL ) - p = strrchr( ofname, ':' ); - if( p == NULL ) - len = 0; - else - len = 1 + (size_t)( p - ofname ); - - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - memcpy( ofn, ofname, len ); - if( p == NULL ) - p = ofname; - else - ++p; - - if( p[0] == '*' && (!p[1] || p[1] == '.' || p[1] == ';') ) { - if( flags ) - *flags |= OF_WILD; - len = (e == NULL)? strlen(fname) : (size_t)(e - fname); - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - memcpy( ofn+ bufsize[1] -len, fname, len ); - ++p; - } else { - q = strchr( p, '.' ); - if( q == NULL ) - q = strrchr( p, ';' ); - if( q == NULL ) - q = p + strlen( p ); - len = (size_t)(q - p); - - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - memcpy( ofn + bufsize[1] - len, p, len ); - p += len; - } - - if( p[0] == '.' ) { - q = strrchr( p+1, ';' ); - if( q == NULL ) - q = strrchr( p+1, '.' ); - if( q == NULL || q <= p ) - q = p + strlen( p ); - len = (size_t)(q - p ); - if( len == 2 && p[1] == '*' ) { - if( flags ) - *flags |= OF_WILD; - if( e != NULL ) { - len = strlen( e ); - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - memcpy( ofn+ bufsize[1] - len, e, len ); - } - p += 2; - } else { - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - memcpy( ofn+ bufsize[1] - len, p, len ); - p += len; - } - } - if( *p == ';' || *p == '.' ) { - len = strlen( p ); - if( !xpand( &ofn, bufsize, len ) ) { - free( from ); - return NULL; - } - ofn[bufsize[1] - len] = ';'; - memcpy( ofn+ bufsize[1] + 1 - len, p+1, len -1 ); - p += len; - } - if( *p ) - abort(); - - ofn[bufsize[1]] = '\0'; - free( from ); - - return ofn; -} - -/******************************************************************* xpand() */ - -static int xpand( char **buf, size_t size[2], size_t add ) { - char *nbuf; - size_t need; - - need = (size[1] += add) + 1; - if( need < size[0] ) - return 1; - - nbuf = realloc( *buf, need + 32 + 1 ); - if( nbuf == NULL ) { - free( *buf ); - *buf = NULL; - return 0; - } - *buf = nbuf; - size[0] = need + 32 + 1; - return 1; -} + +/* This is 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#if !defined( DEBUG ) && defined( DEBUG_COPYCMD ) +#define DEBUG DEBUG_COPYCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include +#include + +#ifdef _WIN32 +#include +#include "winfile.h" +#define basename winbasename +#undef fstat +#define fstat _fstat +#elif defined(VMS) +#include "vmsfile.h" +#define basename vmsbasename +#else +#include +#include +#include +#endif + +#ifndef GLOB_BRACE +#define GLOB_BRACE 0 +#endif +#ifndef GLOB_TILDE_CHECK +#define GLOB_TILDE_CHECK 0 +#endif + +#include "cmddef.h" + +static vmscond_t copy_from( options_t options, int argc, char **argv ); +static vmscond_t copy_1_from( options_t options, struct FAB *fab, + const char *to, size_t pause ); +static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ); + +static vmscond_t copy_to( options_t options, int argc, char **argv ); +static vmscond_t copy_1_to( options_t options, char *from, char *to ); + +char *output_file_name( const char *ofname, const char *ifname, int *flags ); +#define OF_WILD 1 + +static int xpand( char **buf, size_t size[2], size_t add ); + +/******************************************************************* dotype() */ + +/* Display a file on the terminal */ + +#define type_page OPT_SHARED_20 +#define type_one OPT_SHARED_19 +#define copy_vfc OPT_SHARED_18 + +static uint32_t height; + +qual_t typequals[] = { {"page", type_page, 0, VOPT(DV(&height)), + "-commands type qual_page"}, + {"nopage", 0, type_page, NV, NULL}, + {"onefile", type_one, 0, NV, NULL}, + {"vfc_header", copy_vfc, 0, NV, + "-commands type qual_vfc"}, + {"novfc_header", 0, copy_vfc, NV, NULL}, + + {NULL, 0, 0, NV, NULL } +}; + +param_t typepars[] = { {"filespec", REQ | NOLIM, FSPEC, NOPA, "commands type filespec"}, + + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(type) { + vmscond_t sts; + int theight; + options_t options; + struct FAB fab = cc$rms_fab; + struct NAM nam = cc$rms_nam; + char esa[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; + + (void) termchar( NULL, &theight ); + height = theight-1; + + if( $FAILS(sts = checkquals( &options, type_page, typequals, qualc, qualv )) ) { + return sts; + } + if( !(options & type_page) ) { + height = 0; + } + + --argc; + ++argv; + while( $SUCCESSFUL(sts) && argc-- ) { + nam.nam$l_esa = esa; + nam.nam$b_ess = NAM$C_MAXRSS; + + fab.fab$l_fna = argv++[0]; + fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); + + if( options & type_one ) { /* RMS test/debug: implicit parse/search */ + if( $FAILS(sts = sys_open(&fab)) ) { + nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; + printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); + sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); + break; + } + sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); + sys_close(&fab); + continue; + } + fab.fab$l_nam = &nam; + + if( $FAILS(sts = sys_parse(&fab)) ) { + fab.fab$l_fna[fab.fab$b_fns] = '\0'; + printmsg( TYPE_PARSEFAIL, 0, fab.fab$l_fna ); + sts = printmsg( sts, MSG_CONTINUE, TYPE_PARSEFAIL ); + break; + } + + nam.nam$l_rsa = rsa; + nam.nam$b_rss = NAM$C_MAXRSS; + fab.fab$l_fop = 0; + + while( $SUCCESSFUL(sts) ) { + if( $FAILS(sts = sys_search( &fab )) ) { + if( $MATCHCOND(sts, RMS$_NMF) ) + sts = SS$_NORMAL; + nam.nam$l_esa[nam.nam$b_esl] = '\0'; + printmsg( TYPE_SEARCHFAIL, 0, nam.nam$l_esa ); + sts = printmsg( sts, MSG_CONTINUE, TYPE_SEARCHFAIL ); + break; + } + fab.fab$b_fac = FAB$M_GET; + + if( nam.nam$l_fnb & NAM$M_WILDCARD ) + printf( "\n%.*s\n\n", nam.nam$b_rsl, rsa ); + + if( $FAILS(sts = sys_open(&fab)) ) { + nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; + printmsg( TYPE_OPENIN, 0, nam.nam$l_rsa ); + sts = printmsg( sts, MSG_CONTINUE, TYPE_OPENIN ); + break; + } + sts = copy_1_from( options & copy_vfc, &fab, NULL, height ); + + sys_close(&fab); + } + } + printf( "\n" ); + + fab.fab$b_fns = + nam.nam$b_ess = + fab.fab$b_dns = 0; + nam.nam$l_rlf = NULL; + nam.nam$b_nop = NAM$M_SYNCHK; + (void) sys_parse( &fab ); /* Release context */ + + if( $MATCHCOND( sts, RMS$_EOF ) ) + sts = SS$_NORMAL | STS$M_INHIB_MSG; + + return sts; +} + +/******************************************************************* docopy() */ + +/* Copy files to and from Files-11 volume */ + +#define copy_binary OPT_GENERIC_1 +#define copy_log OPT_GENERIC_2 +#define copy_tof11 OPT_GENERIC_3 +#define copy_confirm OPT_GENERIC_4 +#define copy_override OPT_GENERIC_5 + +#define copy_owner OPT_GENERIC_6 +#define copy_protect OPT_GENERIC_7 +#define copy_volume OPT_GENERIC_8 +#define copy_verlim OPT_GENERIC_9 + +#define copy_recfmt (OPT_GENERIC_12|OPT_GENERIC_11|OPT_GENERIC_10) +#define copy_v_recfmt (OPT_V_GENERIC + 9) + +/* GENERIC option space is full */ + +#define copy_b4 (OPT_SHARED_2|OPT_SHARED_1) +#define copy_v_b4 0 + +#define copy_after (OPT_SHARED_4|OPT_SHARED_3) +#define copy_v_after 2 + +#define copy_prn_none 0 +#define copy_prn_nl 1 +#define copy_prn_c0 2 +#define copy_prn_vfu 3 + +#define copy_prn OPT_SHARED_5 +#define copy_ftn OPT_SHARED_6 +#define copy_cr OPT_SHARED_7 +#define copy_span OPT_SHARED_8 +#define copy_append OPT_SHARED_9 + +/* Type uses OPT_SHARED from the high end */ + +static uint32_t b4n, aftn; +static uint32_t vfclen; +static uint32_t reclen; +static uint32_t verlimit; +#if 0 +static uint32_t volume; +#endif +static uint32_t protection; +static uint32_t ownuic; + +/* Print CC */ +static qual_t +prnb4[] = { {"none", copy_prn_none << copy_v_b4, + copy_b4, NV, "commands copy qual_cc print b4 none"}, + {"nl", copy_prn_nl << copy_v_b4, + copy_b4, DV(&b4n), "commands copy qual_cc print b4 nl"}, + {"ctl", copy_prn_c0, copy_b4, DV(&b4n), "commands copy qual_cc print b4 ctl"}, + {"vfu", copy_prn_vfu << copy_v_b4, + copy_b4, DV(&b4n), "commands copy qual_cc print b4 vfu"}, + + {NULL, 0, 0, NV, NULL} +}; +static qual_t /* Same keywords as prnb4. Use same help. */ +prnafter[] = { {"none", copy_prn_none << copy_v_after, + copy_after, NV, "commands copy qual_cc print b4 none"}, + {"nl", copy_prn_nl << copy_v_after, + copy_after, DV(&aftn), "commands copy qual_cc print b4 nl"}, + {"ctl", copy_prn_c0, copy_b4, DV(&aftn), "commands copy qual_cc print b4 ctl"}, + {"vfu", copy_prn_vfu << copy_v_after, + copy_after, DV(&aftn), "commands copy qual_cc print b4 vfu"}, + + {NULL, 0, 0, NV, NULL} +}; + +static qual_t +prnkwds[] = { {"before", 0, copy_b4, KV(prnb4), "commands copy qual_cc print b4"}, + {"after", 0, copy_after, KV(prnafter), "commands copy qual_cc print after"}, + + {NULL, OPT_NOSORT, 0, NV, NULL} +}; + +/* Carriage control record attributes */ +static qual_t +cckwds[] = { {"fortran", copy_ftn, copy_cr|copy_prn, NV, + "commands copy qual_cc fortran"}, + {"carriage_return", copy_cr, copy_ftn|copy_prn, NV, + "commands copy qual_cc cr"}, + {"print", copy_prn|(FAB$C_VFC<qualc, copy_defopt->qualv )) ) { + return sts; + } + } + else + options = copy_defaults; + + if( $FAILS(sts = checkquals( &options, options, copyquals, qualc, qualv )) ) { + return sts; + } + + if( options & copy_tof11 ) + return copy_to( options, argc, argv ); + + return copy_from( options, argc, argv ); + +} + +/******************************************************************* copy_from() */ + +static vmscond_t copy_from( options_t options, int argc, char **argv ) { + vmscond_t sts; + struct NAM nam; + struct FAB fab; + char res[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; + int i, filecount = 0; + vmscond_t confirm = ODS2_CONFIRM_ALL; + + if( options & copy_confirm ) + confirm = COPY_CONFIRM; + + sts = SS$_NORMAL; + + nam = cc$rms_nam; + fab = cc$rms_fab; + + for( i = 1; i < argc -1; i++ ) { + 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 = (uint8_t)strlen(fab.fab$l_fna); + + if( $FAILS(sts = sys_parse(&fab)) ) { + printmsg( COPY_PARSEFAIL, 0, fab.fab$l_fna ); + sts = printmsg( sts, MSG_CONTINUE, COPY_PARSEFAIL ); + break; + } + res[nam.nam$b_esl] = '\0'; + + nam.nam$l_rsa = rsa; + nam.nam$b_rss = NAM$C_MAXRSS; + fab.fab$l_fop = 0; + + while( $SUCCESSFUL(sts) ) { + char name[NAM$C_MAXRSS + 1]; + char *out, *inp; + int dot = FALSE; + uint32_t directory = 0; + uint16_t retlen; + struct XABPRO pro; + struct XABDAT dat; + struct XABITM itm; + struct item_list xitems[] = { + { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, + { 0, 0, NULL, 0 } + }; + + if( $FAILS(sts = sys_search( &fab )) ) { + if( $MATCHCOND(sts, RMS$_NMF) ) { + sts = SS$_NORMAL; + break; + } + nam.nam$l_esa[nam.nam$b_esl] = '\0'; + printmsg( COPY_SEARCHFAIL, 0, nam.nam$l_esa ); + sts = printmsg( sts, MSG_CONTINUE, COPY_SEARCHFAIL ); + break; + } + rsa[nam.nam$b_rsl] = '\0'; + + pro = cc$rms_xabpro; + dat = cc$rms_xabdat; + itm = cc$rms_xabitm; + + dat.xab$l_nxt = &pro; + itm.xab$l_nxt = &dat; + xitems[0].buffer = &directory; + xitems[0].retlen = &retlen; + itm.xab$b_mode = XAB$K_SENSEMODE; + itm.xab$l_itemlist = xitems; + fab.fab$l_xab = &itm; + fab.fab$b_fac = FAB$M_GET; + + sts = sys_open(&fab); + fab.fab$l_xab = NULL; + rsa[nam.nam$b_rsl] = '\0'; + + if( $FAILED(sts) ) { + int err; + err = errno; + printmsg( COPY_OPENIN, 0, rsa ); + sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); + if( err ) + printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENIN, + strerror( err ) ); + break; + } + + if( directory ) { + if( !(options & copy_override) ) { + printmsg( COPY_DIRNOTCOPIED, 0, rsa ); + sys_close( &fab ); + continue; + } + } + + out = name; + inp = argv[2]; + + 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 { + size_t length; + + length = nam.nam$b_name; + + if (*inp == '\0') + length += nam.nam$b_type; + memcpy( out, nam.nam$l_name, length ); + out += length; + } + continue; + } + if (*inp == '.') { + dot = TRUE; + } else { + if (strchr(":]\\/", *inp)) + dot = FALSE; + } + *out++ = *inp++; + } + *out++ = '\0'; + + if( $MATCHCOND( confirm, COPY_CONFIRM) ) + confirm = confirm_cmd( confirm, rsa, name ); + + if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { + if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { + confirm = COPY_CONFIRM; + } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { + confirm = COPY_CONFIRM; + sys_close( &fab ); + continue; + } else { /* ODS2_CONFIRM_QUIT */ + sys_close( &fab ); + sts = confirm; + break; + } + } + + fab.fab$l_xab = &itm; + if( $SUCCESSFUL(sts = copy_1_from( options, &fab, name, 0 )) ) + filecount++; + fab.fab$l_xab = NULL; + + sys_close(&fab); + } /* search */ + if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || + $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) + break; + } /* arg */ + + if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { + if (filecount > 0) { + if( options & copy_log ) + sts = printmsg( COPY_COPIED, 0, filecount ); + } else { + sts = printmsg( COPY_NOFILES, 0 ); + } + } + return sts; +} + +/******************************************************************* copy_1_from() */ + +static vmscond_t copy_1_from( options_t options, struct FAB *fab, + const char *to, size_t dopause ) { + vmscond_t sts; + uint32_t records = 0; + int stream = 0, var = 0; + FILE *tof; + char *rec; + pause_t *pause = NULL, pausectl; + struct RAB rab = cc$rms_rab; + + if( dopause ) { + pausectl.interval = dopause; + pausectl.lineno = 0; + pausectl.prompt = TYPE_CONTINUE; + pause = &pausectl; + } + + rab.rab$l_fab = fab; + + if( $FAILS(sts = sys_connect(&rab)) ) { + printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); + sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); + return sts; + } + + if( (rec = malloc( MAXREC + 2 )) == NULL ) { + sys_disconnect( &rab ); + printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); + sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, COPY_OPENIN ); + return sts; + } + + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + rab.rab$l_rhb = NULL; + + if( options & copy_binary ) { + fab->fab$b_rfm = FAB$C_UDF; + fab->fab$b_rat = 0; + } else { + switch( fab->fab$b_rfm ) { + case FAB$C_STMLF: + stream = '\n'; + break; + case FAB$C_STMCR: + stream = '\r'; + break; + case FAB$C_STM: + stream = EOF; + break; + case FAB$C_VFC: + var = 1; + if( fab-> fab$b_fsz != 0 ) { + ++var; + rab.rab$l_rhb = malloc( fab->fab$b_fsz ); + if( rab.rab$l_rhb == NULL ) { + free( rec ); + sys_disconnect( &rab ); + printmsg( COPY_OPENIN, 0, fab->fab$l_fna ); + sts = printmsg( sts, MSG_CONTINUE, COPY_OPENIN ); + return sts; + } + } + break; + case FAB$C_VAR: + var = 1; + break; + case FAB$C_FIX: + break; + } + } + + if( to == NULL ) + tof = stdout; + else { + if( (stream || + (fab->fab$b_rat & (FAB$M_CR | FAB$M_PRN | FAB$M_FTN))) && + !(options & copy_binary) ) { + tof = openf( to, (options & copy_append)? "a": "w" ); + } else { + tof = openf( to, (options & copy_append)? "ab": "wb" ); + } + } + if (tof == NULL) { + int err; + + err = errno; + free( rec ); + free( rab.rab$l_rhb ); + sys_disconnect( &rab ); + printmsg( COPY_OPENOUT, 0, to ); + return printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_OPENOUT, + strerror( err ) ); + } + + while( $SUCCESSFUL(sts) ) { + char *rp; + unsigned rsz; + + if( ferror( tof ) ) { + int err; + + err = errno; + printmsg( COPY_WRITEERR, 0, (to == NULL)? "tty": to ); + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_WRITEERR, + strerror(err) ); + break; + } + if( $FAILS(sts = sys_get( &rab )) ) { + if( $MATCHCOND(sts, RMS$_EOF) ) + sts |= STS$M_INHIB_MSG; + else { + printmsg( COPY_READERR, 0, fab->fab$l_fna ); + sts = printmsg( sts, MSG_CONTINUE, COPY_READERR, rab.rab$l_stv); + } + break; + } + rp = rec; + rsz = rab.rab$w_rsz; + + if( (options & copy_binary) || fab->fab$b_rfm == FAB$C_UDF ) { + if( rsz != 0 ) { + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + if( fab->fab$b_rfm == FAB$C_UDF ) + records += rsz; + else + records++; + } + continue; + } + + if( stream ) { + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + + if( fab->fab$b_rat & (FAB$M_CR|FAB$M_FTN) ) { + if( stream == EOF ) { + if( $FAILS(sts = put_strn( "\r\n", 2, tof, pause )) ) + break; + } else { + if( $FAILS(sts = put_c( stream, tof, pause )) ) + break; + } + } + ++records; + continue; + } + + if( fab->fab$b_rat & FAB$M_FTN ) { + --rsz; + switch( *rp++ ) { /* AA-D034E-TE Table 8-5 */ + case '+': /* Overprint current line and return to left margin */ + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + break; + case ' ': /* Single space: output to beginning of next line */ + default: + if( $FAILS(sts = put_c( '\n', tof, pause )) ) + break; + if( $FAILS(sts = put_strn(rp, rsz, tof, pause )) ) + break; + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + break; + case '0': /* Double space: skip a line before starting output */ + if( $FAILS(sts = put_strn( "\n\n", 2, tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + break; + case '1': /* Paging: start output at top of a new page */ + if( $FAILS(sts = put_c( '\f', tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + break; + case '$': /* Prompting: Start a beginning of next line and suppress \r */ + if( $FAILS(sts = put_c( '\n', tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + break; + case '\0': /* Overprinting with no advance - no \r at end */ + if( $FAILS(sts = put_c( '\r', tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + break; + } + if( $FAILED(sts) ) + break; + ++records; + continue; + } + + if( (fab->fab$b_rat & FAB$M_CR) && $FAILS(sts = put_c( '\n', tof, pause )) ) + break; + + if( var == 2 ) { + if( (fab->fab$b_rat & FAB$M_PRN) && fab->fab$b_fsz == 2 ) { + if( $FAILS(sts = put_prn( rab.rab$l_rhb[0], tof, pause )) ) + break; + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + if( $FAILS(sts = put_prn( rab.rab$l_rhb[1], tof, pause )) ) + break; + ++records; + continue; + } + + if( (options & copy_vfc) && + $FAILS(sts = put_strn( rab.rab$l_rhb, + fab->fab$b_fsz, tof, pause )) ) + break; + } + + if( $FAILS(sts = put_strn( rp, rsz, tof, pause )) ) + break; + + if( fab->fab$b_rat & FAB$M_CR ) + fputc( '\r', tof ); + + ++records; + } + + sys_disconnect( &rab ); + + if( var == 2 ) { + free( rab.rab$l_rhb ); + rab.rab$l_rhb = NULL; + } + + free( rec ); + rec = NULL; + + if( to == NULL ) { /* stdout */ + if( $FAILED(sts) ) + sts = printmsg( sts, 0, rab.rab$l_stv ); + return sts; + } + if( fclose( tof ) ) { + int err; + + err = errno; + printmsg( COPY_CLOSEOUT, 0, (to == NULL)? "tty": to ); + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_CLOSEOUT, strerror( err ) ); + return sts; + } + + do { + struct XABDAT *dat; + time_t posixtime; + unsigned short timvec[7]; + char *tz; + struct tm tm; +#ifdef _WIN32 + struct _utimbuf tv; +#else + struct timeval tv[2]; +#endif + + for( dat = (struct XABDAT *)fab->fab$l_xab; + dat != NULL && dat->xab$b_cod != XAB$C_DAT; dat = dat->xab$l_nxt ) + ; + + if( dat == NULL ) + break; + + if( $FAILS(sys_numtim( timvec, dat->xab$q_rdt )) ) + break; + + 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( to, &tv ); +#else + tv[0].tv_sec = posixtime; + tv[0].tv_usec = timvec[6] * 10000; + tv[1] = tv[0]; /* Set mtime to atime */ + (void) utimes( to, tv ); +#endif + } + if( tz != NULL ) + setenv( "TZ", tz, 1 ); + else + unsetenv( "TZ" ); + tzset(); + free( tz ); + + } while( 0 ); + +#ifndef _WIN32 + do { + struct XABPRO *pro; + mode_t mode; + uint16_t prot; + + for( pro = (struct XABPRO *)fab->fab$l_xab; + pro != NULL && pro->xab$b_cod != XAB$C_PRO; pro = pro->xab$l_nxt ) + ; + + if( pro == NULL ) + break; + + mode = (S_IRUSR|S_IWUSR|S_IXUSR| + S_IRGRP|S_IWGRP|S_IXGRP| + S_IROTH|S_IWOTH|S_IXOTH); + + prot = pro->xab$w_pro; + + if( prot & ((xab$m_noread << xab$v_system) | + (xab$m_noread << xab$v_owner)) ) + mode &= ~S_IRUSR; + if( prot & (((xab$m_nodel|xab$m_nowrite) << xab$v_system) | + ((xab$m_nodel|xab$m_nowrite) << xab$v_owner)) ) + mode &= ~S_IWUSR; + if( prot & ( (xab$m_noexe << xab$v_system) | + (xab$m_noexe << xab$v_owner) ) ) + mode &= ~S_IXUSR; + if( prot & (xab$m_noread << xab$v_group) ) + mode &= ~S_IRGRP; + if( prot & (xab$m_nowrite << xab$v_group) ) + mode &= ~S_IWGRP; + if( prot & (xab$m_noexe << xab$v_group) ) + mode &= ~S_IXGRP; + if( prot & (xab$m_noread << xab$v_world) ) + mode &= ~S_IROTH; + if( prot & (xab$m_nowrite << xab$v_world) ) + mode &= ~S_IWOTH; + if( prot & (xab$m_noexe << xab$v_world) ) + mode &= ~S_IXOTH; + if( chmod( to, mode ) != 0 ) { + int err; + + err = errno; + printmsg( COPY_PROTERR, 0, to ); + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, COPY_PROTERR, + strerror( err ) ); + } + } while( 0 ); +#endif + + if( !$MATCHCOND(sts, RMS$_EOF) ) + return sts; + + if( options & copy_log ) { + sts = (fab->fab$b_rfm == FAB$C_UDF)? COPY_COPIEDU: COPY_COPIEDR; + + sts = printmsg( sts, 0, + fab->fab$l_nam->nam$l_rsa, + (to == NULL)? "tty": to, + records ); + } + return SS$_NORMAL; +} + +/******************************************************************* put_prn() */ +static vmscond_t put_prn( char ch, FILE *tof, pause_t *pause ) { + vmscond_t sts; + size_t n; + /* 7 6 5 4 */ + if( !(ch & 0x80) ) { /* 0 x x x */ + n = ch & 0x7F; + while( n-- ) + if( $FAILS(sts = put_c( '\n', tof, pause )) ) + return sts; + return SS$_NORMAL; + } /* 1 x x x */ + if( !(ch & 0x40) ) { /* 1 0 x x */ + if( !(ch & 0x20) ) /* 1 0 0 x */ + return put_c( ch & 0x1f, tof, pause ); /* C0 */ + /* 1 0 1 x */ + return put_c( (ch & 0x1f) | 0x80, tof, pause ); /* C1/reserved */ + } /* 1 1 x x */ + if( !(ch & 0x30) ) { /* 1 1 0 0 */ + static const char vfuch[16] = { + '\f', '\020', '\021', '\022', /* CH 1 - 4 */ + '\023', '\024', '\v', '\n', /* CH 5 - 8 */ + '\n', '\n', '\n', '\n', /* CH 9 - 12 */ + '\n', '\n', '\n', '\n' }; /* CH 13 - 16 */ + if( tof == stdout ) + return put_c( '\n', tof, pause ); /* type: 1 LF */ + return put_c( vfuch[ch & 0xf], tof, pause ); + /* VFU channel 1+<3:0> */ + } /* 1 1 y y : yy == 01, 10, 11 */ + return put_c( ch, tof, pause ); /* Device-specific/reserved -- Agrave - yumlaut */ +} + +/******************************************************************* copy_to() */ + +static vmscond_t copy_to( options_t options, int argc, char **argv ) { + glob_t globs; + int i, err, gflags = GLOB_MARK | GLOB_BRACE | GLOB_TILDE_CHECK; + size_t n, filecount = 0; + vmscond_t sts; + vmscond_t confirm = ODS2_CONFIRM_ALL; + + memset( &globs, 0, sizeof( globs ) ); + + if( options & copy_confirm ) + confirm = COPY_CONFIRM; + + sts = SS$_NORMAL; + + for( i = 1; i < argc -1; i++ ) { + err = glob( argv[i], gflags, NULL, &globs ); + gflags |= GLOB_APPEND; + switch( err ) { + case 0: + sts = SS$_NORMAL; + break; + case GLOB_NOSPACE: + sts = printmsg( COPY_NOMEM, 0, argv[i] ); + break; + case GLOB_ABORTED: + sts = printmsg( COPY_GLOBABT, 0, argv[i] ); + break; + case GLOB_NOMATCH: + sts = printmsg( COPY_NOMATCH, 0, argv[i] ); + break; + default: + sts = printmsg( COPY_GLOBERR, 0, err, argv[i] ); + break; + } + if( $FAILED(sts) ) { + globfree( &globs ); + return sts; + } + } + for( n = 0; n < globs.gl_pathc; n++ ) { + char *to; + + if( (to = output_file_name( argv[ argc - 1], + globs.gl_pathv[n], NULL )) == NULL ) { + sts = printmsg( COPY_NOMEM, 0, globs.gl_pathv[n] ); + globfree( &globs ); + return sts; + } + + if( $MATCHCOND( confirm, COPY_CONFIRM) ) + confirm = confirm_cmd( confirm, globs.gl_pathv[n], to ); + + if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { + if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { + confirm = COPY_CONFIRM; + } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { + confirm = COPY_CONFIRM; + free( to ); + continue; + } else { /* ODS2_CONFIRM_QUIT */ + sts = confirm; + free( to ); + break; + } + } + sts = copy_1_to( options, globs.gl_pathv[n], to ); + + free( to ); + if( $SUCCESSFUL(sts ) ) + ++filecount; + else if( $VMS_STATUS_SEVERITY(sts) > STS$K_WARNING || + $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) + break; + } /* glob */ + + if( $SUCCESSFUL(sts) || $MATCHCOND(sts, ODS2_CONFIRM_QUIT) ) { + if( filecount > 0 ) { + if( options & copy_log ) + sts = printmsg( COPY_COPIED, 0, filecount ); + } else { + sts = printmsg( COPY_NOFILES, 0 ); + } + } + globfree( &globs ); + return sts; +} + +/******************************************************************* copy_1_to() */ +static vmscond_t copy_1_to( options_t options, char *from, char *to ) { + vmscond_t sts; + size_t records = 0, bufsize = 80; + int keepnl = 0; + char *buf = NULL; + FILE *fromf; + struct FAB fab; + struct RAB rab; + struct NAM nam; + struct XABDAT dat; + struct XABPRO pro; + char vfc[2], rsbuf[NAM$C_MAXRSS+1]; +#ifdef _WIN32 + struct _stat statbuf; +#else + struct stat statbuf; +#endif + + fab = cc$rms_fab; + rab = cc$rms_rab; + nam = cc$rms_nam; + dat = cc$rms_xabdat; + pro = cc$rms_xabpro; + + if ( *from == '\0' ) { + return printmsg( COPY_NONAME, 0 ); + } + fromf = openf( from, (options & copy_binary)? "rb": "r" ); + if( fromf == NULL ) { + int err; + + err = errno; + sts = printmsg( COPY_OPENIN, 0, from ); + if( err ) + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, + COPY_OPENIN, strerror(err) ); + return sts; + } + if( fstat( Fileno( fromf ), &statbuf ) != 0 ) + statbuf.st_size = 0; + else { + unsigned short tvec[7]; + +#ifdef _WIN32 + struct tm tmd; + struct tm *tm = &tmd; + (void) gmtime_s( &tmd, &statbuf.st_mtime ); +#else + struct tm *tm; + + tm = gmtime( &statbuf.st_mtime ); +#endif + tvec[0] = tm->tm_year + 1900; + tvec[1] = tm->tm_mon + 1; + tvec[2] = tm->tm_mday; + tvec[3] = tm->tm_hour; + tvec[4] = tm->tm_min; + tvec[5] = tm->tm_sec; + tvec[6] = 0; + + if( $SUCCESSFUL(lib_cvt_vectim( tvec, dat.xab$q_rdt )) ) { +#ifdef _WIN32 + (void) gmtime_s( &tmd, &statbuf.st_ctime ); + tvec[0] = tm->tm_year + 1900; + tvec[1] = tm->tm_mon + 1; + tvec[2] = tm->tm_mday; + tvec[3] = tm->tm_hour; + tvec[4] = tm->tm_min; + tvec[5] = tm->tm_sec; + tvec[6] = 0; + + (void) lib_cvt_vectim( tvec, dat.xab$q_cdt ); +#else /* No local create time */ + memcpy( &dat.xab$q_cdt, dat.xab$q_rdt, sizeof( dat.xab$q_cdt ) ); +#endif + + fab.fab$l_xab = &dat; + } + } + + nam.nam$l_rsa = rsbuf; + nam.nam$b_rss = (uint8_t)(sizeof( rsbuf ) -1); + + if( options & (copy_protect|copy_owner) ) { + pro.xab$l_nxt = fab.fab$l_xab; + fab.fab$l_xab = &pro; + + if( options & copy_protect ) { + pro.xab$w_pro = (uint16_t) + ((pro.xab$w_pro & ~(protection >> 16)) | + (uint16_t)protection); + } + + if( options & copy_owner ) { + pro.xab$l_uic = ownuic; + } + } + + fab.fab$l_nam = &nam; + + 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 0 + if( options & copy_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_STMLF; + } +#endif + + if( !(options & copy_recfmt) ) { + options |= (FAB$C_VAR +1) << copy_v_recfmt; + if( !(options & (copy_prn|copy_ftn|copy_cr)) ) + options |= copy_cr; + } + + fab.fab$b_rfm = ((options & copy_recfmt) >> copy_v_recfmt) -1; + + switch( fab.fab$b_rfm ) { + case FAB$C_STM: + case FAB$C_STMLF: + case FAB$C_STMCR: + fab.fab$b_rat |= FAB$M_CR; + keepnl = 1; + break; + case FAB$C_UDF: + keepnl = 1; + break; + default: + keepnl - 0; + break; + } + + if( !(options & copy_span) ) + fab.fab$b_rat |= FAB$M_BLK; + if( options & copy_ftn ) + fab.fab$b_rat |= FAB$M_FTN; + if( options & copy_cr ) + fab.fab$b_rat |= FAB$M_CR; + if( options & copy_prn ) { + fab.fab$b_rat |= FAB$M_PRN; + fab.fab$b_fsz = 2; + fab.fab$b_rfm = FAB$C_VFC; + + switch( (options & copy_b4) >> copy_v_b4 ) { + case copy_prn_none: + vfc[0] = 0; + break; + case copy_prn_nl: + if( b4n < 1 || b4n > 127 ) + return printmsg( COPY_INVALIDNL, 0 ); + vfc[0] = b4n & 0x7f; + break; + case copy_prn_c0: + if( b4n > 31 ) + return printmsg( COPY_INVALIDC0, 0 ); + vfc[0] = 0x80 | b4n; + break; + case copy_prn_vfu: + if( b4n < 1 || b4n > 16 ) + return printmsg( COPY_INVALIDVFU, 0 ); + vfc[0] = (0xC0 | (b4n -1)) & 0xff; + } + switch( (options & copy_after) >> copy_v_after ) { + case copy_prn_none: + vfc[1] = 0; + break; + case copy_prn_nl: + if( aftn < 1 || aftn > 127 ) + return printmsg( COPY_INVALIDNL, 0 ); + vfc[1] = aftn & 0x7f; + break; + case copy_prn_c0: + if( aftn > 31 ) + return printmsg( COPY_INVALIDC0, 0 ); + vfc[1] = 0x80 | aftn; + break; + case copy_prn_vfu: + if( aftn < 1 || aftn > 16 ) + return printmsg( COPY_INVALIDVFU, 0 ); + vfc[1] = (0xC0 | (aftn -1)) & 0xff; + } + } /* prn */ + + fab.fab$l_alq = (statbuf.st_size + 511) / 512; + fab.fab$w_mrs = (uint16_t)reclen; + + fab.fab$l_fna = to; + fab.fab$b_fns = (uint8_t)strlen( fab.fab$l_fna ); + + do { + if( options & copy_append ) { + sts = sys_open( &fab ); + rab.rab$l_rop = RAB$M_EOF; + } else + sts = sys_create( &fab ); + rsbuf[nam.nam$b_rsl] = '\0'; + if( $FAILED(sts) ) { + fclose( fromf ); + printmsg( COPY_OPENOUT, 0, (nam.nam$b_rsl? rsbuf:to) ); + return printmsg( sts, MSG_CONTINUE, COPY_OPENOUT ); + } + + rab.rab$l_fab = &fab; + rab.rab$b_rac = RAB$C_SEQ; + + if( $FAILS(sts = sys_connect( &rab )) ) { + sts = printmsg( sts, 0 ); + break; + } + + if( fab.fab$b_rfm == FAB$C_VFC ) { + if( options & copy_prn ) + rab.rab$l_rhb = vfc; + } else + options &= ~(copy_prn|copy_vfc); + + if( options & copy_binary ) { + char *buf; + uint16_t len; + + if( (buf = malloc( 512 )) == NULL ) { + sts = printmsg( COPY_NOMEM, 0, from ); + break; + } + rab.rab$l_rbf = buf; + while( (len = (uint16_t)fread( buf, 1, 512, fromf )) > 0 ) { + if( len != 512 ) + memset( buf + len, 0, 512 - len ); + rab.rab$w_rsz = 512; + + if( $FAILS(sts = sys_put( &rab )) ) { + printmsg( COPY_WRITEERR, 0, rsbuf ); + sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); + break; + } + ++records; + } + free( buf ); + buf = NULL; + break; + } + while( (rab.rab$l_rbf = fgetline( fromf, keepnl, &buf, &bufsize )) != NULL ) { + size_t len; + char * rp; + + rp = rab.rab$l_rbf; + len = strlen( rp ); + if( (options & (copy_prn | copy_vfc)) == copy_vfc ) { + if( len < fab.fab$b_fsz ) { + sts = RMS$_RSZ; + break; + } + rab.rab$l_rhb = rp; + rab.rab$l_rbf = rp + fab.fab$b_fsz; + len -= fab.fab$b_fsz; + } + + do { + rab.rab$w_rsz = (uint16_t) (len % (MAXREC + 1)); + sts = sys_put( &rab ); + rab.rab$l_rbf += rab.rab$w_rsz; + len -= rab.rab$w_rsz; + } while( len > 0 && $SUCCESSFUL( sts ) ); + ++records; + if( $FAILED(sts) ) { + printmsg( COPY_WRITEERR, 0, rsbuf ); + sts = printmsg( sts, MSG_CONTINUE, COPY_WRITEERR ); + break; + } + } + } while( 0 ); + + if( buf != NULL ) free( buf ); + sys_disconnect( &rab ); + { + vmscond_t clsts; + + if( $FAILS(clsts = sys_close( &fab )) && $SUCCESSFUL(sts) ) { + printmsg( COPY_CLOSEOUT, 0, rsbuf ); + clsts = printmsg( clsts, MSG_CONTINUE, COPY_CLOSEOUT ); + } + if( $SUCCESSFUL(sts) ) + sts = clsts; + } + fclose( fromf ); + + if( options & copy_log ) { + sts = (options & copy_binary)? COPY_COPIEDB: COPY_COPIEDR; + + sts = printmsg( sts, 0, + from, fab.fab$l_nam->nam$l_rsa, + records ); + } + + return sts; +} + +/******************************************************************* output_file_name() */ + +char *output_file_name( const char *ofname, const char *ifname, int *flags ) { + char *ofn, *from, *fname; + const char *p, *q, *e; + size_t len, bufsize[2]; + + if( flags != NULL ) + *flags = 0; + + ofn= NULL; + bufsize[0] = + bufsize[1] = 0; + + len = strlen( ifname ) +1; + if( (from = malloc( len )) == NULL ) { + return NULL; + } + memcpy( from, ifname, len ); +#ifdef _WIN32 + for( fname = from; *fname != '\0'; fname++ ) + if( *fname == '/' ) + *fname = '\\'; +#endif + + fname = basename( from ); + e = strrchr( fname, '.' ); + + p = strrchr( ofname, ']' ); + if( p == NULL ) + p = strrchr( ofname, ':' ); + if( p == NULL ) + len = 0; + else + len = 1 + (size_t)( p - ofname ); + + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + memcpy( ofn, ofname, len ); + if( p == NULL ) + p = ofname; + else + ++p; + + if( p[0] == '*' && (!p[1] || p[1] == '.' || p[1] == ';') ) { + if( flags ) + *flags |= OF_WILD; + len = (e == NULL)? strlen(fname) : (size_t)(e - fname); + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + memcpy( ofn+ bufsize[1] -len, fname, len ); + ++p; + } else { + q = strchr( p, '.' ); + if( q == NULL ) + q = strrchr( p, ';' ); + if( q == NULL ) + q = p + strlen( p ); + len = (size_t)(q - p); + + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + memcpy( ofn + bufsize[1] - len, p, len ); + p += len; + } + + if( p[0] == '.' ) { + q = strrchr( p+1, ';' ); + if( q == NULL ) + q = strrchr( p+1, '.' ); + if( q == NULL || q <= p ) + q = p + strlen( p ); + len = (size_t)(q - p ); + if( len == 2 && p[1] == '*' ) { + if( flags ) + *flags |= OF_WILD; + if( e != NULL ) { + len = strlen( e ); + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + memcpy( ofn+ bufsize[1] - len, e, len ); + } + p += 2; + } else { + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + memcpy( ofn+ bufsize[1] - len, p, len ); + p += len; + } + } + if( *p == ';' || *p == '.' ) { + len = strlen( p ); + if( !xpand( &ofn, bufsize, len ) ) { + free( from ); + return NULL; + } + ofn[bufsize[1] - len] = ';'; + memcpy( ofn+ bufsize[1] + 1 - len, p+1, len -1 ); + p += len; + } + if( *p ) + abort(); + + ofn[bufsize[1]] = '\0'; + free( from ); + + return ofn; +} + +/******************************************************************* xpand() */ + +static int xpand( char **buf, size_t size[2], size_t add ) { + char *nbuf; + size_t need; + + need = (size[1] += add) + 1; + if( need < size[0] ) + return 1; + + nbuf = realloc( *buf, need + 32 + 1 ); + if( nbuf == NULL ) { + free( *buf ); + *buf = NULL; + return 0; + } + *buf = nbuf; + size[0] = need + 32 + 1; + return 1; +} diff --git a/extracters/ods2/createcmd.c b/extracters/ods2/createcmd.c index 5920d82..464d06d 100644 --- a/extracters/ods2/createcmd.c +++ b/extracters/ods2/createcmd.c @@ -1,673 +1,673 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#if !defined( DEBUG ) && defined( DEBUG_CREATECMD ) -#define DEBUG DEBUG_CREATECMD -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include "cmddef.h" - -static vmscond_t create_directory( options_t options, int argc, char** argv ); -static vmscond_t create_file( options_t options, int argc, char** argv ); - - -/******************************************************************* docreate() */ - -#define create_log OPT_GENERIC_1 -#define create_dir OPT_GENERIC_2 - -#define create_owner OPT_GENERIC_3 -#define create_protect OPT_GENERIC_4 -#define create_volume OPT_GENERIC_5 -#define create_verlim OPT_GENERIC_6 - -#define create_recfmt (OPT_GENERIC_10|OPT_GENERIC_9|OPT_GENERIC_8) -#define create_v_recfmt (OPT_V_GENERIC + 7) - -#define create_b4 (OPT_SHARED_2|OPT_SHARED_1) -#define create_v_b4 0 - -#define create_after (OPT_SHARED_4|OPT_SHARED_3) -#define create_v_after 2 - -#define create_prn_none 0 -#define create_prn_nl 1 -#define create_prn_c0 2 -#define create_prn_vfu 3 - -#define create_prn OPT_SHARED_17 -#define create_ftn OPT_SHARED_18 -#define create_cr OPT_SHARED_19 -#define create_span OPT_SHARED_20 - -static uint32_t b4n, aftn; -static uint32_t vfclen; -static uint32_t reclen; -static uint32_t verlimit; -static uint32_t volume; -static uint32_t protection; -static uint32_t ownuic; - -static qual_t -prnb4[] = { {"none", create_prn_none << create_v_b4, - create_b4, NV, "commands create qual_cc print b4 none"}, - {"nl", create_prn_nl << create_v_b4, - create_b4, DV(&b4n), "commands create qual_cc print b4 nl"}, - {"ctl", create_prn_c0, create_b4, DV(&b4n), "commands create qual_cc print b4 ctl"}, - {"vfu", create_prn_vfu << create_v_b4, - create_b4, DV(&b4n), "commands create qual_cc print b4 vfu"}, - - {NULL, 0, 0, NV, NULL} -}; -static qual_t -prnafter[] = { {"none", create_prn_none << create_v_after, - create_after, NV, "commands create qual_cc print b4 none"}, - {"nl", create_prn_nl << create_v_after, - create_after, DV(&aftn), "commands create qual_cc print b4 nl"}, - {"ctl", create_prn_c0, create_b4, DV(&aftn), "commands create qual_cc print b4 ctl"}, - {"vfu", create_prn_vfu << create_v_after, - create_after, DV(&aftn), "commands create qual_cc print b4 vfu"}, - - {NULL, 0, 0, NV, NULL} -}; - -static qual_t -prnkwds[] = { {"before", 0, create_b4, KV(prnb4), "commands create qual_cc print b4"}, - {"after", 0, create_after, KV(prnafter), "commands create qual_cc print after"}, - - {NULL, OPT_NOSORT, 0, NV, NULL} -}; - -static qual_t -cckwds[] = { {"fortran", create_ftn, create_cr|create_prn, NV, - "commands create qual_cc fortran"}, - {"carriage_return", create_cr, create_ftn|create_prn, NV, - "commands create qual_cc cr"}, - {"print", create_prn|(FAB$C_VFC<> 16)) | - (uint16_t)protection); - } - - if( options & create_owner ) { - pro.xab$l_uic = ownuic; - } - - itm.xab$l_nxt = &fhc; - - directory = 1; - itm.xab$b_mode = XAB$K_SETMODE; - pro.xab$l_nxt = &itm; - nfab.fab$l_xab = &pro; - - nam.nam$b_rss = sizeof( rsname ) -1; - nam.nam$l_rsa = rsname; - nfab.fab$l_nam = &nam; - - nfab.fab$b_fac = 0; - nfab.fab$l_fop = FAB$M_OFP; - nfab.fab$b_org = FAB$C_SEQ; - nfab.fab$b_rat = FAB$M_BLK; - nfab.fab$b_rfm = FAB$C_VAR; - fhc.xab$w_lrl = - nfab.fab$w_mrs = 512; - - nfab.fab$b_fns = fab.fab$b_fns; - nfab.fab$l_fna = fab.fab$l_fna; - - sts = sys_create( &nfab ); - - rsname[nam.nam$b_rsl] = '\0'; - if( $FAILED(sts) ) { - printmsg( CREATE_CREDIRFAIL, 0, rsname ); - sts = printmsg( sts, MSG_CONTINUE, CREATE_CREDIRFAIL ); - } else { - if( $FAILS(sts = sys_close(&nfab)) ) { - printmsg(CREATE_CLOSEOUT, 0, rsname); - sts = printmsg(sts, MSG_CONTINUE, CREATE_CLOSEOUT); - } - } - nfab.fab$b_fns = - nam.nam$b_ess = 0; - nfab.fab$b_dns = 0; - nam.nam$b_nop = NAM$M_SYNCHK; - (void) sys_parse( &nfab ); - - if( $FAILED(sts) ) - break; - - if( options & create_log ) - sts = printmsg( CREATE_CREATED, 0, rsname ); - } - - if( *p == ']' ) - break; - - switch( level++ ) { - case 0: - break; - case 1: - memmove( tdir+devlen+1, tdir+devlen+tmplen, sfdlen ); - tmplen = 1+ sfdlen +1; - tdir[devlen+tmplen-1] = ']'; - break; - default: - tdir[devlen+tmplen -1] = '.'; - tmplen += 1 + sfdlen; - tdir[devlen+tmplen -1] = ']'; - break; - } - - for( sfdlen = 0; *p != '.' && *p != ']'; sfdlen++ ) - tdir[devlen+tmplen+sfdlen] = *p++; - - memcpy( tdir+devlen+tmplen+sfdlen, ".DIR;1", 6 ); - if( *p == '.' ) - ++p; - } - - } while( 0 ); - - if( nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE | - NAM$M_EXP_VER | NAM$M_WILDCARD) ) { - sts = printmsg( RMS$_DIR, 0 ); - } - - fab.fab$b_fns = - nam.nam$b_ess = - fab.fab$b_dns = 0; - nam.nam$b_nop = NAM$M_SYNCHK; - (void) sys_parse( &fab ); - - return sts; -} - -/******************************************************************* create_file() */ - -static vmscond_t create_file( options_t options, int argc, char** argv ) { - vmscond_t sts; - char *buf = NULL; - size_t bufsize = 80; - char vfc[2], rsbuf[NAM$C_MAXRSS + 1]; - struct NAM nam; - struct FAB fab; - struct RAB rab; - struct XABPRO pro; - - UNUSED( argc ); - - nam = cc$rms_nam; - fab = cc$rms_fab; - rab = cc$rms_rab; - pro = cc$rms_xabpro; - - nam.nam$l_esa = NULL; - nam.nam$b_ess = 0; - - nam.nam$b_rss = sizeof( rsbuf ) -1; - nam.nam$l_rsa = rsbuf; - - if( options & (create_protect|create_owner) ) { - fab.fab$l_xab = &pro; - - if( options & create_protect ) { - pro.xab$w_pro = (uint16_t) - ((pro.xab$w_pro & ~(protection >> 16)) | - (uint16_t)protection); - } - - if( options & create_owner ) { - pro.xab$l_uic = ownuic; - } - } - - fab.fab$l_nam = &nam; - - 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 & create_recfmt) ) { - options |= (FAB$C_VAR +1) << create_v_recfmt; - if( !(options & (create_prn|create_ftn|create_cr)) ) - options |= create_cr; - } - - fab.fab$b_rfm = ((options & create_recfmt) >> create_v_recfmt) -1; - - switch( fab.fab$b_rfm ) { - case FAB$C_STM: - case FAB$C_STMLF: - case FAB$C_STMCR: - fab.fab$b_rat |= FAB$M_CR; - break; - case FAB$C_VFC: - fab.fab$b_fsz = vfclen; - break; - } - - if( !(options & create_span) ) - fab.fab$b_rat |= FAB$M_BLK; - if( options & create_ftn ) - fab.fab$b_rat |= FAB$M_FTN; - if( options & create_cr ) - fab.fab$b_rat |= FAB$M_CR; - if( options & create_prn ) { - fab.fab$b_rat |= FAB$M_PRN; - fab.fab$b_fsz = 2; - fab.fab$b_rfm = FAB$C_VFC; - - switch( (options & create_b4) >> create_v_b4 ) { - case create_prn_none: - vfc[0] = 0; - break; - case create_prn_nl: - if( b4n < 1 || b4n > 127 ) - return printmsg( CREATE_INVALIDNL, 0 ); - vfc[0] = b4n & 0x7f; - break; - case create_prn_c0: - if( b4n > 31 ) - return printmsg( CREATE_INVALIDC0, 0 ); - vfc[0] = 0x80 | b4n; - break; - case create_prn_vfu: - if( b4n < 1 || b4n > 16 ) - return printmsg( CREATE_INVALIDVFU, 0 ); - vfc[0] = (0xC0 | (b4n -1)) & 0xff; - } - switch( (options & create_after) >> create_v_after ) { - case create_prn_none: - vfc[1] = 0; - break; - case create_prn_nl: - if( aftn < 1 || aftn > 127 ) - return printmsg( CREATE_INVALIDNL, 0 ); - vfc[1] = aftn & 0x7f; - break; - case create_prn_c0: - if( aftn > 31 ) - return printmsg( CREATE_INVALIDC0, 0 ); - vfc[1] = 0x80 | aftn; - break; - case create_prn_vfu: - if( aftn < 1 || aftn > 16 ) - return printmsg( CREATE_INVALIDVFU, 0 ); - vfc[1] = (0xC0 | (aftn -1)) & 0xff; - } - } /* prn */ - - /* - fab.fab$w_deq = 5; - fab.fab$l_alq = 100; - */ - fab.fab$w_mrs = (uint16_t)reclen; - - fab.fab$l_fna = argv[1]; - fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); - - do { - char *input; - - sts = sys_create( &fab ); - rsbuf[nam.nam$b_rsl] = '\0'; - - if( $FAILED(sts) ) - break; - - rab.rab$l_fab = &fab; - if( $FAILS(sts = sys_connect( &rab )) ) - break; - if( options & create_prn ) - rab.rab$l_rhb = vfc; - if( options & create_log ) { - printmsg( CREATE_READY, 0, rsbuf ); - printmsg( CREATE_TERMEOF, MSG_CONTINUE, CREATE_READY, -#if defined( VMS ) || defined( _WIN32 ) - "Control-Z" -#ifdef _WIN32 - " Enter" -#endif -#else - "Control-D" -#endif - ); - } - - while( (input = fgetline( stdin, FALSE, &buf, &bufsize )) != NULL ) { - size_t len; - - len = strlen( input ); - sts = SS$_NORMAL; - -#if 0 - if( len && input[len -1] == '\r' ) - len--; -#endif - rab.rab$l_rbf = input; - - switch( fab.fab$b_rfm ) { - case FAB$C_FIX: - if( len > (size_t)fab.fab$w_mrs ) - len = fab.fab$w_mrs; - else if( len < (size_t)fab.fab$w_mrs ) { - char *p = input; - - if( bufsize < fab.fab$w_mrs ) { - p = realloc( buf, fab.fab$w_mrs ); - if( p == NULL ) { - sts = SS$_INSFMEM; - break; - } - bufsize = fab.fab$w_mrs; - rab.rab$l_rbf = - buf = - input = p; - } - p += len; - len = fab.fab$w_mrs - len; - while( len-- ) - *p++ = ' '; - len = fab.fab$w_mrs; - } - break; - case FAB$C_VFC: - if( !(options & create_prn) ) { - if( len < fab.fab$b_fsz ) { - sts = RMS$_RFM; - break; - } - rab.rab$l_rhb = input; - rab.rab$l_rbf = input + fab.fab$b_fsz; - len -= fab.fab$b_fsz; - } - break; - } - if( $SUCCESSFUL(sts) ) { - rab.rab$w_rsz = (uint16_t)len; - - sts = sys_put(&rab); - } - - if( $FAILED(sts) ) - break; - } - break; - } while( 0 ); - - if( buf != NULL ) free( buf ); - - if( $FAILED(sts) ) { - vmscond_t rsts; - int err; - - err = errno; - rsts = printmsg( sts, 0, nam.nam$b_rsl? rsbuf: argv[1] ); - if( err ) - printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror( err ) ); - sts = rsts; - } - sys_disconnect( &rab ); - if( $SUCCESSFUL( sts ) ) { - if( $FAILS(sts = sys_close( &fab )) ) { - vmscond_t rsts; - int err; - - err = errno; - rsts = printmsg( CREATE_CLOSEOUT, 0, - nam.nam$b_rsl? rsbuf: argv[1] ); - printmsg( sts, MSG_CONTINUE, rsts, nam.nam$b_rsl? rsbuf: argv[1] ); - if( err ) - printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror( err ) ); - sts = rsts; - } - } else { - sys_close( &fab ); - } - - if( $SUCCESSFUL( sts ) && (options & create_log) ) - sts = printmsg( CREATE_CREATED, 0, rsbuf ); - - return sts; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#if !defined( DEBUG ) && defined( DEBUG_CREATECMD ) +#define DEBUG DEBUG_CREATECMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +static vmscond_t create_directory( options_t options, int argc, char** argv ); +static vmscond_t create_file( options_t options, int argc, char** argv ); + + +/******************************************************************* docreate() */ + +#define create_log OPT_GENERIC_1 +#define create_dir OPT_GENERIC_2 + +#define create_owner OPT_GENERIC_3 +#define create_protect OPT_GENERIC_4 +#define create_volume OPT_GENERIC_5 +#define create_verlim OPT_GENERIC_6 + +#define create_recfmt (OPT_GENERIC_10|OPT_GENERIC_9|OPT_GENERIC_8) +#define create_v_recfmt (OPT_V_GENERIC + 7) + +#define create_b4 (OPT_SHARED_2|OPT_SHARED_1) +#define create_v_b4 0 + +#define create_after (OPT_SHARED_4|OPT_SHARED_3) +#define create_v_after 2 + +#define create_prn_none 0 +#define create_prn_nl 1 +#define create_prn_c0 2 +#define create_prn_vfu 3 + +#define create_prn OPT_SHARED_17 +#define create_ftn OPT_SHARED_18 +#define create_cr OPT_SHARED_19 +#define create_span OPT_SHARED_20 + +static uint32_t b4n, aftn; +static uint32_t vfclen; +static uint32_t reclen; +static uint32_t verlimit; +static uint32_t volume; +static uint32_t protection; +static uint32_t ownuic; + +static qual_t +prnb4[] = { {"none", create_prn_none << create_v_b4, + create_b4, NV, "commands create qual_cc print b4 none"}, + {"nl", create_prn_nl << create_v_b4, + create_b4, DV(&b4n), "commands create qual_cc print b4 nl"}, + {"ctl", create_prn_c0, create_b4, DV(&b4n), "commands create qual_cc print b4 ctl"}, + {"vfu", create_prn_vfu << create_v_b4, + create_b4, DV(&b4n), "commands create qual_cc print b4 vfu"}, + + {NULL, 0, 0, NV, NULL} +}; +static qual_t +prnafter[] = { {"none", create_prn_none << create_v_after, + create_after, NV, "commands create qual_cc print b4 none"}, + {"nl", create_prn_nl << create_v_after, + create_after, DV(&aftn), "commands create qual_cc print b4 nl"}, + {"ctl", create_prn_c0, create_b4, DV(&aftn), "commands create qual_cc print b4 ctl"}, + {"vfu", create_prn_vfu << create_v_after, + create_after, DV(&aftn), "commands create qual_cc print b4 vfu"}, + + {NULL, 0, 0, NV, NULL} +}; + +static qual_t +prnkwds[] = { {"before", 0, create_b4, KV(prnb4), "commands create qual_cc print b4"}, + {"after", 0, create_after, KV(prnafter), "commands create qual_cc print after"}, + + {NULL, OPT_NOSORT, 0, NV, NULL} +}; + +static qual_t +cckwds[] = { {"fortran", create_ftn, create_cr|create_prn, NV, + "commands create qual_cc fortran"}, + {"carriage_return", create_cr, create_ftn|create_prn, NV, + "commands create qual_cc cr"}, + {"print", create_prn|(FAB$C_VFC<> 16)) | + (uint16_t)protection); + } + + if( options & create_owner ) { + pro.xab$l_uic = ownuic; + } + + itm.xab$l_nxt = &fhc; + + directory = 1; + itm.xab$b_mode = XAB$K_SETMODE; + pro.xab$l_nxt = &itm; + nfab.fab$l_xab = &pro; + + nam.nam$b_rss = sizeof( rsname ) -1; + nam.nam$l_rsa = rsname; + nfab.fab$l_nam = &nam; + + nfab.fab$b_fac = 0; + nfab.fab$l_fop = FAB$M_OFP; + nfab.fab$b_org = FAB$C_SEQ; + nfab.fab$b_rat = FAB$M_BLK; + nfab.fab$b_rfm = FAB$C_VAR; + fhc.xab$w_lrl = + nfab.fab$w_mrs = 512; + + nfab.fab$b_fns = fab.fab$b_fns; + nfab.fab$l_fna = fab.fab$l_fna; + + sts = sys_create( &nfab ); + + rsname[nam.nam$b_rsl] = '\0'; + if( $FAILED(sts) ) { + printmsg( CREATE_CREDIRFAIL, 0, rsname ); + sts = printmsg( sts, MSG_CONTINUE, CREATE_CREDIRFAIL ); + } else { + if( $FAILS(sts = sys_close(&nfab)) ) { + printmsg(CREATE_CLOSEOUT, 0, rsname); + sts = printmsg(sts, MSG_CONTINUE, CREATE_CLOSEOUT); + } + } + nfab.fab$b_fns = + nam.nam$b_ess = 0; + nfab.fab$b_dns = 0; + nam.nam$b_nop = NAM$M_SYNCHK; + (void) sys_parse( &nfab ); + + if( $FAILED(sts) ) + break; + + if( options & create_log ) + sts = printmsg( CREATE_CREATED, 0, rsname ); + } + + if( *p == ']' ) + break; + + switch( level++ ) { + case 0: + break; + case 1: + memmove( tdir+devlen+1, tdir+devlen+tmplen, sfdlen ); + tmplen = 1+ sfdlen +1; + tdir[devlen+tmplen-1] = ']'; + break; + default: + tdir[devlen+tmplen -1] = '.'; + tmplen += 1 + sfdlen; + tdir[devlen+tmplen -1] = ']'; + break; + } + + for( sfdlen = 0; *p != '.' && *p != ']'; sfdlen++ ) + tdir[devlen+tmplen+sfdlen] = *p++; + + memcpy( tdir+devlen+tmplen+sfdlen, ".DIR;1", 6 ); + if( *p == '.' ) + ++p; + } + + } while( 0 ); + + if( nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE | + NAM$M_EXP_VER | NAM$M_WILDCARD) ) { + sts = printmsg( RMS$_DIR, 0 ); + } + + fab.fab$b_fns = + nam.nam$b_ess = + fab.fab$b_dns = 0; + nam.nam$b_nop = NAM$M_SYNCHK; + (void) sys_parse( &fab ); + + return sts; +} + +/******************************************************************* create_file() */ + +static vmscond_t create_file( options_t options, int argc, char** argv ) { + vmscond_t sts; + char *buf = NULL; + size_t bufsize = 80; + char vfc[2], rsbuf[NAM$C_MAXRSS + 1]; + struct NAM nam; + struct FAB fab; + struct RAB rab; + struct XABPRO pro; + + UNUSED( argc ); + + nam = cc$rms_nam; + fab = cc$rms_fab; + rab = cc$rms_rab; + pro = cc$rms_xabpro; + + nam.nam$l_esa = NULL; + nam.nam$b_ess = 0; + + nam.nam$b_rss = sizeof( rsbuf ) -1; + nam.nam$l_rsa = rsbuf; + + if( options & (create_protect|create_owner) ) { + fab.fab$l_xab = &pro; + + if( options & create_protect ) { + pro.xab$w_pro = (uint16_t) + ((pro.xab$w_pro & ~(protection >> 16)) | + (uint16_t)protection); + } + + if( options & create_owner ) { + pro.xab$l_uic = ownuic; + } + } + + fab.fab$l_nam = &nam; + + 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 & create_recfmt) ) { + options |= (FAB$C_VAR +1) << create_v_recfmt; + if( !(options & (create_prn|create_ftn|create_cr)) ) + options |= create_cr; + } + + fab.fab$b_rfm = ((options & create_recfmt) >> create_v_recfmt) -1; + + switch( fab.fab$b_rfm ) { + case FAB$C_STM: + case FAB$C_STMLF: + case FAB$C_STMCR: + fab.fab$b_rat |= FAB$M_CR; + break; + case FAB$C_VFC: + fab.fab$b_fsz = vfclen; + break; + } + + if( !(options & create_span) ) + fab.fab$b_rat |= FAB$M_BLK; + if( options & create_ftn ) + fab.fab$b_rat |= FAB$M_FTN; + if( options & create_cr ) + fab.fab$b_rat |= FAB$M_CR; + if( options & create_prn ) { + fab.fab$b_rat |= FAB$M_PRN; + fab.fab$b_fsz = 2; + fab.fab$b_rfm = FAB$C_VFC; + + switch( (options & create_b4) >> create_v_b4 ) { + case create_prn_none: + vfc[0] = 0; + break; + case create_prn_nl: + if( b4n < 1 || b4n > 127 ) + return printmsg( CREATE_INVALIDNL, 0 ); + vfc[0] = b4n & 0x7f; + break; + case create_prn_c0: + if( b4n > 31 ) + return printmsg( CREATE_INVALIDC0, 0 ); + vfc[0] = 0x80 | b4n; + break; + case create_prn_vfu: + if( b4n < 1 || b4n > 16 ) + return printmsg( CREATE_INVALIDVFU, 0 ); + vfc[0] = (0xC0 | (b4n -1)) & 0xff; + } + switch( (options & create_after) >> create_v_after ) { + case create_prn_none: + vfc[1] = 0; + break; + case create_prn_nl: + if( aftn < 1 || aftn > 127 ) + return printmsg( CREATE_INVALIDNL, 0 ); + vfc[1] = aftn & 0x7f; + break; + case create_prn_c0: + if( aftn > 31 ) + return printmsg( CREATE_INVALIDC0, 0 ); + vfc[1] = 0x80 | aftn; + break; + case create_prn_vfu: + if( aftn < 1 || aftn > 16 ) + return printmsg( CREATE_INVALIDVFU, 0 ); + vfc[1] = (0xC0 | (aftn -1)) & 0xff; + } + } /* prn */ + + /* + fab.fab$w_deq = 5; + fab.fab$l_alq = 100; + */ + fab.fab$w_mrs = (uint16_t)reclen; + + fab.fab$l_fna = argv[1]; + fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); + + do { + char *input; + + sts = sys_create( &fab ); + rsbuf[nam.nam$b_rsl] = '\0'; + + if( $FAILED(sts) ) + break; + + rab.rab$l_fab = &fab; + if( $FAILS(sts = sys_connect( &rab )) ) + break; + if( options & create_prn ) + rab.rab$l_rhb = vfc; + if( options & create_log ) { + printmsg( CREATE_READY, 0, rsbuf ); + printmsg( CREATE_TERMEOF, MSG_CONTINUE, CREATE_READY, +#if defined( VMS ) || defined( _WIN32 ) + "Control-Z" +#ifdef _WIN32 + " Enter" +#endif +#else + "Control-D" +#endif + ); + } + + while( (input = fgetline( stdin, FALSE, &buf, &bufsize )) != NULL ) { + size_t len; + + len = strlen( input ); + sts = SS$_NORMAL; + +#if 0 + if( len && input[len -1] == '\r' ) + len--; +#endif + rab.rab$l_rbf = input; + + switch( fab.fab$b_rfm ) { + case FAB$C_FIX: + if( len > (size_t)fab.fab$w_mrs ) + len = fab.fab$w_mrs; + else if( len < (size_t)fab.fab$w_mrs ) { + char *p = input; + + if( bufsize < fab.fab$w_mrs ) { + p = realloc( buf, fab.fab$w_mrs ); + if( p == NULL ) { + sts = SS$_INSFMEM; + break; + } + bufsize = fab.fab$w_mrs; + rab.rab$l_rbf = + buf = + input = p; + } + p += len; + len = fab.fab$w_mrs - len; + while( len-- ) + *p++ = ' '; + len = fab.fab$w_mrs; + } + break; + case FAB$C_VFC: + if( !(options & create_prn) ) { + if( len < fab.fab$b_fsz ) { + sts = RMS$_RFM; + break; + } + rab.rab$l_rhb = input; + rab.rab$l_rbf = input + fab.fab$b_fsz; + len -= fab.fab$b_fsz; + } + break; + } + if( $SUCCESSFUL(sts) ) { + rab.rab$w_rsz = (uint16_t)len; + + sts = sys_put(&rab); + } + + if( $FAILED(sts) ) + break; + } + break; + } while( 0 ); + + if( buf != NULL ) free( buf ); + + if( $FAILED(sts) ) { + vmscond_t rsts; + int err; + + err = errno; + rsts = printmsg( sts, 0, nam.nam$b_rsl? rsbuf: argv[1] ); + if( err ) + printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror( err ) ); + sts = rsts; + } + sys_disconnect( &rab ); + if( $SUCCESSFUL( sts ) ) { + if( $FAILS(sts = sys_close( &fab )) ) { + vmscond_t rsts; + int err; + + err = errno; + rsts = printmsg( CREATE_CLOSEOUT, 0, + nam.nam$b_rsl? rsbuf: argv[1] ); + printmsg( sts, MSG_CONTINUE, rsts, nam.nam$b_rsl? rsbuf: argv[1] ); + if( err ) + printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror( err ) ); + sts = rsts; + } + } else { + sys_close( &fab ); + } + + if( $SUCCESSFUL( sts ) && (options & create_log) ) + sts = printmsg( CREATE_CREATED, 0, rsbuf ); + + return sts; +} diff --git a/extracters/ods2/debug.c b/extracters/ods2/debug.c index 5d3d3cf..e214d9a 100644 --- a/extracters/ods2/debug.c +++ b/extracters/ods2/debug.c @@ -1,125 +1,125 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - - -#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; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + + +#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 index e14179c..55367c2 100644 --- a/extracters/ods2/debug.h +++ b/extracters/ods2/debug.h @@ -1,15 +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 +#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 index a17a7cd..c5692d7 100644 --- a/extracters/ods2/deletecmd.c +++ b/extracters/ods2/deletecmd.c @@ -1,179 +1,179 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#if !defined( DEBUG ) && defined( DEBUG_DELETECMD ) -#define DEBUG DEBUG_DELETECMD -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include "cmddef.h" - -#ifdef _WIN32 -#undef DELETE -#endif - -/***************************************************************** dodelete() */ - -#define delete_log OPT_GENERIC_1 -#define delete_confirm OPT_GENERIC_2 - -qual_t delquals[] = { {"log", delete_log, 0, NV, - "-commands delete qual_log"}, - {"nolog", 0, delete_log, NV, NULL }, - {"confirm", delete_confirm, 0, NV, - "-commands delete qual_confirm"}, - {"noconfirm", 0, delete_confirm, NV, NULL}, - { NULL, 0, 0, NV, NULL } -}; -param_t delpars[] = { {"filespec", REQ | NOLIM, FSPEC, NOPA, - "commands delete filespec"}, - { NULL, 0, 0, NOPA, NULL } -}; - -DECL_CMD(delete) { - vmscond_t sts = 0; - char esa[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; - struct NAM nam = cc$rms_nam; - struct FAB fab = cc$rms_fab; - options_t options, filecount = 0; - vmscond_t confirm = ODS2_CONFIRM_ALL; - - if( $FAILS(sts = checkquals( &options, delete_log, delquals, qualc, qualv )) ) { - return sts; - } - - if( options & delete_confirm ) - confirm = DELETE_CONFIRM; - - --argc; - ++argv; - while( ($SUCCESSFUL(sts) || - $VMS_STATUS_SEVERITY(sts) <= STS$K_WARNING ) && argc-- ) { - nam.nam$l_esa = esa; - nam.nam$b_ess = sizeof(esa) -1; - fab.fab$l_nam = &nam; - fab.fab$l_fna = argv++[0]; - fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); - - if( $FAILS(sts = sys_parse( &fab )) ) { - printmsg( DELETE_NOTDELETED, 0, fab.fab$l_fna ); - sts = printmsg( sts,MSG_CONTINUE, DELETE_NOTDELETED ); - break; - } - esa[nam.nam$b_esl] = '\0'; - if( !(nam.nam$l_fnb & NAM$M_EXP_VER) ) { - sts = printmsg( $SETFAC(SHR$_DELVER,DELETE), 0 ); - break; - } - nam.nam$l_rsa = rsa; - nam.nam$b_rss = sizeof(rsa) -1; - fab.fab$l_fop = 0; - - while( TRUE ) { - uint32_t directory; - uint16_t retlen; - struct FAB ffab = cc$rms_fab; - struct RAB rab = cc$rms_rab; - struct XABITM itm = cc$rms_xabitm; - struct item_list xitems[] = { - { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, - { 0, 0, NULL, 0 } - }; - - if( $FAILS(sts = sys_search( &fab )) ) { - if( $MATCHCOND(sts, RMS$_NMF) ) - break; - printmsg( DELETE_NOTDELETED, 0, - (nam.nam$b_esl? esa: fab.fab$l_fna) ); - sts = printmsg( sts, MSG_CONTINUE, DELETE_NOTDELETED ); - break; - } - rsa[nam.nam$b_rsl] = '\0'; - - if( $MATCHCOND( confirm, DELETE_CONFIRM) ) - confirm = confirm_cmd( confirm, rsa ); - - if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { - if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { - confirm = DELETE_CONFIRM; - } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { - confirm = DELETE_CONFIRM; - continue; - } else { /* ODS2_CONFIRM_QUIT */ - sts = confirm; - break; - } - } - - xitems[0].buffer = &directory; xitems[0].retlen = &retlen; - itm.xab$b_mode = XAB$K_SENSEMODE; - itm.xab$l_itemlist = xitems; - ffab.fab$l_xab = &itm; - ffab.fab$l_fna = rsa; - ffab.fab$b_fns = nam.nam$b_rsl; - ffab.fab$b_fac = FAB$M_GET; - - if( $FAILS(sys_open( &ffab )) ) { - sts = printmsg( DELETE_OPENIN, 0, rsa ); - continue; - } - if( directory ) { - rab.rab$l_fab = &ffab; - if( $FAILS(sts = sys_connect( &rab )) ) { - printmsg( DELETE_OPENIN, 0, rsa ); - sts = printmsg( sts, MSG_CONTINUE, DELETE_OPENIN ); - } else { - sts = sys_get( &rab ); - if( $MATCHCOND( sts, RMS$_EOF ) ) { - sts = SS$_NORMAL; - } else { /* RMS$_RTB if a record exists */ - printmsg( DELETE_NOTDELETED, 0, rsa ); - sts = printmsg( DELETE_DIRNOTEMPTY, MSG_CONTINUE, DELETE_NOTDELETED ); - } - sys_disconnect( &rab ); - } - } - sys_close( &ffab ); - if( $FAILED(sts) ) { - continue; - } - if( $FAILS(sts = sys_erase(&ffab)) ) { - printmsg( DELETE_NOTDELETED, 0, rsa ); - sts = printmsg( sts, MSG_CONTINUE, DELETE_NOTDELETED ); - continue; - } - filecount++; - if( options & delete_log ) - sts = printmsg( DELETE_DELETED, 0, rsa); - } /* search */ - if( $MATCHCOND( sts, ODS2_CONFIRM_QUIT ) ) - break; - if( $MATCHCOND(sts, RMS$_NMF) ) - sts = SS$_NORMAL; - } /* arg */ - - if( $SUCCESSFUL(sts) || $MATCHCOND( sts, ODS2_CONFIRM_QUIT ) ) { - if( filecount < 1 ) - sts = printmsg( DELETE_NOFILES, 0 ); - else if( filecount != 1 ) - sts = printmsg( DELETE_NFILES, 0, filecount ); - } - fab.fab$b_fns = - fab.fab$b_dns = 0; - nam.nam$b_nop = NAM$M_SYNCHK; - (void) sys_parse( &fab ); /* Discard context */ - - return sts; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DELETECMD ) +#define DEBUG DEBUG_DELETECMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#ifdef _WIN32 +#undef DELETE +#endif + +/***************************************************************** dodelete() */ + +#define delete_log OPT_GENERIC_1 +#define delete_confirm OPT_GENERIC_2 + +qual_t delquals[] = { {"log", delete_log, 0, NV, + "-commands delete qual_log"}, + {"nolog", 0, delete_log, NV, NULL }, + {"confirm", delete_confirm, 0, NV, + "-commands delete qual_confirm"}, + {"noconfirm", 0, delete_confirm, NV, NULL}, + { NULL, 0, 0, NV, NULL } +}; +param_t delpars[] = { {"filespec", REQ | NOLIM, FSPEC, NOPA, + "commands delete filespec"}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(delete) { + vmscond_t sts = 0; + char esa[NAM$C_MAXRSS + 1], rsa[NAM$C_MAXRSS + 1]; + struct NAM nam = cc$rms_nam; + struct FAB fab = cc$rms_fab; + options_t options, filecount = 0; + vmscond_t confirm = ODS2_CONFIRM_ALL; + + if( $FAILS(sts = checkquals( &options, delete_log, delquals, qualc, qualv )) ) { + return sts; + } + + if( options & delete_confirm ) + confirm = DELETE_CONFIRM; + + --argc; + ++argv; + while( ($SUCCESSFUL(sts) || + $VMS_STATUS_SEVERITY(sts) <= STS$K_WARNING ) && argc-- ) { + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof(esa) -1; + fab.fab$l_nam = &nam; + fab.fab$l_fna = argv++[0]; + fab.fab$b_fns = (uint8_t)strlen(fab.fab$l_fna); + + if( $FAILS(sts = sys_parse( &fab )) ) { + printmsg( DELETE_NOTDELETED, 0, fab.fab$l_fna ); + sts = printmsg( sts,MSG_CONTINUE, DELETE_NOTDELETED ); + break; + } + esa[nam.nam$b_esl] = '\0'; + if( !(nam.nam$l_fnb & NAM$M_EXP_VER) ) { + sts = printmsg( $SETFAC(SHR$_DELVER,DELETE), 0 ); + break; + } + nam.nam$l_rsa = rsa; + nam.nam$b_rss = sizeof(rsa) -1; + fab.fab$l_fop = 0; + + while( TRUE ) { + uint32_t directory; + uint16_t retlen; + struct FAB ffab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + struct XABITM itm = cc$rms_xabitm; + struct item_list xitems[] = { + { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, + { 0, 0, NULL, 0 } + }; + + if( $FAILS(sts = sys_search( &fab )) ) { + if( $MATCHCOND(sts, RMS$_NMF) ) + break; + printmsg( DELETE_NOTDELETED, 0, + (nam.nam$b_esl? esa: fab.fab$l_fna) ); + sts = printmsg( sts, MSG_CONTINUE, DELETE_NOTDELETED ); + break; + } + rsa[nam.nam$b_rsl] = '\0'; + + if( $MATCHCOND( confirm, DELETE_CONFIRM) ) + confirm = confirm_cmd( confirm, rsa ); + + if( !$MATCHCOND( confirm, ODS2_CONFIRM_ALL ) ) { + if( $MATCHCOND( confirm, ODS2_CONFIRM_YES ) ) { + confirm = DELETE_CONFIRM; + } else if( $MATCHCOND( confirm, ODS2_CONFIRM_NO ) ) { + confirm = DELETE_CONFIRM; + continue; + } else { /* ODS2_CONFIRM_QUIT */ + sts = confirm; + break; + } + } + + xitems[0].buffer = &directory; xitems[0].retlen = &retlen; + itm.xab$b_mode = XAB$K_SENSEMODE; + itm.xab$l_itemlist = xitems; + ffab.fab$l_xab = &itm; + ffab.fab$l_fna = rsa; + ffab.fab$b_fns = nam.nam$b_rsl; + ffab.fab$b_fac = FAB$M_GET; + + if( $FAILS(sys_open( &ffab )) ) { + sts = printmsg( DELETE_OPENIN, 0, rsa ); + continue; + } + if( directory ) { + rab.rab$l_fab = &ffab; + if( $FAILS(sts = sys_connect( &rab )) ) { + printmsg( DELETE_OPENIN, 0, rsa ); + sts = printmsg( sts, MSG_CONTINUE, DELETE_OPENIN ); + } else { + sts = sys_get( &rab ); + if( $MATCHCOND( sts, RMS$_EOF ) ) { + sts = SS$_NORMAL; + } else { /* RMS$_RTB if a record exists */ + printmsg( DELETE_NOTDELETED, 0, rsa ); + sts = printmsg( DELETE_DIRNOTEMPTY, MSG_CONTINUE, DELETE_NOTDELETED ); + } + sys_disconnect( &rab ); + } + } + sys_close( &ffab ); + if( $FAILED(sts) ) { + continue; + } + if( $FAILS(sts = sys_erase(&ffab)) ) { + printmsg( DELETE_NOTDELETED, 0, rsa ); + sts = printmsg( sts, MSG_CONTINUE, DELETE_NOTDELETED ); + continue; + } + filecount++; + if( options & delete_log ) + sts = printmsg( DELETE_DELETED, 0, rsa); + } /* search */ + if( $MATCHCOND( sts, ODS2_CONFIRM_QUIT ) ) + break; + if( $MATCHCOND(sts, RMS$_NMF) ) + sts = SS$_NORMAL; + } /* arg */ + + if( $SUCCESSFUL(sts) || $MATCHCOND( sts, ODS2_CONFIRM_QUIT ) ) { + if( filecount < 1 ) + sts = printmsg( DELETE_NOFILES, 0 ); + else if( filecount != 1 ) + sts = printmsg( DELETE_NFILES, 0, filecount ); + } + fab.fab$b_fns = + fab.fab$b_dns = 0; + nam.nam$b_nop = NAM$M_SYNCHK; + (void) sys_parse( &fab ); /* Discard context */ + + return sts; +} diff --git a/extracters/ods2/descrip.h b/extracters/ods2/descrip.h index a56a0e2..4e186d5 100644 --- a/extracters/ods2/descrip.h +++ b/extracters/ods2/descrip.h @@ -1,50 +1,50 @@ -/* Descrip.h Definitions for descriptors */ - -/* - 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 +/* Descrip.h Definitions for descriptors */ + +/* + 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 contributions of the original author and - * subsequent contributors. - - 1.4 Changed declarations NOT to define DOLLAR identifiers - unless DOLLAR is defined (some compilers can't handle $'s!) - Fixed definition of _DESCRIPTOR(). - 1.4A Changed default DOLLAR handling to include DOLLAR - identifers unless NO_DOLLAR is defined - (ie #ifdef DOLLAR to #ifndef NO_DOLLAR) -*/ - -#ifndef _DESCRIP_H -#define _DESCRIP_H - -#ifndef NO_DOLLAR -#ifndef $DESCRIPTOR -#define DSC$K_DTYPE_T DSC_K_DTYPE_T -#define DSC$K_CLASS_S DSC_K_CLASS_S -#define dsc$descriptor dsc_descriptor -#define dsc$w_length dsc_w_length -#define dsc$b_dtype dsc_b_dtype -#define dsc$b_class dsc_b_class -#define dsc$a_pointer dsc_a_pointer -#define $DESCRIPTOR _DESCRIPTOR -#endif -#endif - -#define DSC_K_DTYPE_T 0 -#define DSC_K_CLASS_S 0 - -struct dsc_descriptor { - unsigned short dsc_w_length; - unsigned char dsc_b_dtype; - unsigned char dsc_b_class; - char *dsc_a_pointer; -}; - -#define _DESCRIPTOR(symbol,value) \ - struct dsc_descriptor symbol = {sizeof(value)-1,0,0,value} - -#endif /* #ifndef _DESCRIP_H */ + * subsequent contributors. + + 1.4 Changed declarations NOT to define DOLLAR identifiers + unless DOLLAR is defined (some compilers can't handle $'s!) + Fixed definition of _DESCRIPTOR(). + 1.4A Changed default DOLLAR handling to include DOLLAR + identifers unless NO_DOLLAR is defined + (ie #ifdef DOLLAR to #ifndef NO_DOLLAR) +*/ + +#ifndef _DESCRIP_H +#define _DESCRIP_H + +#ifndef NO_DOLLAR +#ifndef $DESCRIPTOR +#define DSC$K_DTYPE_T DSC_K_DTYPE_T +#define DSC$K_CLASS_S DSC_K_CLASS_S +#define dsc$descriptor dsc_descriptor +#define dsc$w_length dsc_w_length +#define dsc$b_dtype dsc_b_dtype +#define dsc$b_class dsc_b_class +#define dsc$a_pointer dsc_a_pointer +#define $DESCRIPTOR _DESCRIPTOR +#endif +#endif + +#define DSC_K_DTYPE_T 0 +#define DSC_K_CLASS_S 0 + +struct dsc_descriptor { + unsigned short dsc_w_length; + unsigned char dsc_b_dtype; + unsigned char dsc_b_class; + char *dsc_a_pointer; +}; + +#define _DESCRIPTOR(symbol,value) \ + struct dsc_descriptor symbol = {sizeof(value)-1,0,0,value} + +#endif /* #ifndef _DESCRIP_H */ diff --git a/extracters/ods2/device.c b/extracters/ods2/device.c index 9ffa29b..612b87b 100644 --- a/extracters/ods2/device.c +++ b/extracters/ods2/device.c @@ -1,146 +1,146 @@ -/* Device.c Module to remember and find devices...*/ - -/* - * 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -/* Should have mechanism to return actual device name... */ - -/* This module is simple enough - it just keeps track of - * device names and initialization... - */ - -#include -#include -#include -#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( uint32_t devsiz, void *keyval, vmscond_t *retsts ); -static int device_compare( uint32_t keylen, void *keyval, void *node ); - -static struct DEV *dev_root = NULL; - - -/************************************************************ device_create() */ - -/* device_create() creates a device object... - */ - -static void *device_create( uint32_t devsiz, void *keyval, vmscond_t *retsts ) { - - register struct DEV *dev; - - dev = (struct DEV *) calloc( 1, offsetof( struct DEV, devnam ) + devsiz + 2 ); - if ( dev == NULL ) { - *retsts = SS$_INSFMEM; - return NULL; - } - dev->cache.objtype = OBJTYPE_DEV; - 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, TRUE ); - 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... */ - -static int device_compare( uint32_t keylen, void *keyval, void *node ) { - - register int cmp; - register const char *keynam; - register const char *devnam; - - keynam = (const char *) keyval; - devnam = ((const struct DEV *) node)->devnam; - - cmp = 0; - while ( keylen-- > 0 ) { - cmp = toupper( *keynam++ ) - toupper( *devnam++ ); - if ( cmp != 0 ) { - break; - } - } - if ( cmp == 0 && *devnam != '\0' && *devnam != ':' ) { - cmp = -1; - } - return cmp; -} - -/************************************************************ device_lookup() */ - -/* device_lookup() is to to find devices... */ - -vmscond_t device_lookup( size_t devlen, char *devnam, int create, - struct DEV **retdev ) { - - vmscond_t sts; - size_t devsiz; - register struct DEV *dev; - - for ( devsiz = 0; devsiz < devlen; devsiz++ ) { - if ( devnam[devsiz] == ':' ) { - break; - } - } - dev = (struct DEV *) cache_find( (void *) &dev_root, (uint32_t)devsiz, devnam, &sts, - device_compare, - create ? device_create : NULL ); - if ( dev == NULL ) { - if ( $MATCHCOND(sts, SS$_ITEMNOTFOUND) ) { - sts = SS$_NOSUCHDEV; - } - } else { - if( retdev == NULL ) - device_done( dev ); - else - *retdev = dev; - sts = SS$_NORMAL; - } - return sts; -} +/* Device.c Module to remember and find devices...*/ + +/* + * 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +/* Should have mechanism to return actual device name... */ + +/* This module is simple enough - it just keeps track of + * device names and initialization... + */ + +#include +#include +#include +#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( uint32_t devsiz, void *keyval, vmscond_t *retsts ); +static int device_compare( uint32_t keylen, void *keyval, void *node ); + +static struct DEV *dev_root = NULL; + + +/************************************************************ device_create() */ + +/* device_create() creates a device object... + */ + +static void *device_create( uint32_t devsiz, void *keyval, vmscond_t *retsts ) { + + register struct DEV *dev; + + dev = (struct DEV *) calloc( 1, offsetof( struct DEV, devnam ) + devsiz + 2 ); + if ( dev == NULL ) { + *retsts = SS$_INSFMEM; + return NULL; + } + dev->cache.objtype = OBJTYPE_DEV; + 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, TRUE ); + 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... */ + +static int device_compare( uint32_t keylen, void *keyval, void *node ) { + + register int cmp; + register const char *keynam; + register const char *devnam; + + keynam = (const char *) keyval; + devnam = ((const struct DEV *) node)->devnam; + + cmp = 0; + while ( keylen-- > 0 ) { + cmp = toupper( *keynam++ ) - toupper( *devnam++ ); + if ( cmp != 0 ) { + break; + } + } + if ( cmp == 0 && *devnam != '\0' && *devnam != ':' ) { + cmp = -1; + } + return cmp; +} + +/************************************************************ device_lookup() */ + +/* device_lookup() is to to find devices... */ + +vmscond_t device_lookup( size_t devlen, char *devnam, int create, + struct DEV **retdev ) { + + vmscond_t sts; + size_t devsiz; + register struct DEV *dev; + + for ( devsiz = 0; devsiz < devlen; devsiz++ ) { + if ( devnam[devsiz] == ':' ) { + break; + } + } + dev = (struct DEV *) cache_find( (void *) &dev_root, (uint32_t)devsiz, devnam, &sts, + device_compare, + create ? device_create : NULL ); + if ( dev == NULL ) { + if ( $MATCHCOND(sts, SS$_ITEMNOTFOUND) ) { + sts = SS$_NOSUCHDEV; + } + } else { + if( retdev == NULL ) + device_done( dev ); + else + *retdev = dev; + sts = SS$_NORMAL; + } + return sts; +} diff --git a/extracters/ods2/device.h b/extracters/ods2/device.h index d090d1d..6dcd1f9 100644 --- a/extracters/ods2/device.h +++ b/extracters/ods2/device.h @@ -1,68 +1,68 @@ -/* Device.h Definitions for device 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#ifndef _DEVICE_H -#define _DEVICE_H - -#ifdef _WIN32 -#include /* HANDLE */ -#endif - -#include -#include -#include "access.h" -#include "cache.h" -#include "ods2.h" -#include "phyio.h" - -struct DEV { /* Device information */ - struct CACHE cache; - struct VCB *vcb; /* Pointer to volume (if mounted) */ - options_t 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) */ - unsigned blockspersector; /* Device physical sectorsize (blocks) */ - union { - struct { /* Device uses Win32 APIs for physical I/O */ - HANDLE handle; /* Win32 I/O handle */ - } Win32; - struct { /* Device uses ASPI APIs for physical I/O */ - short dtype; /* ASPI disk type */ - short bus; /* ASPI device bus */ - short id; /* ASPI device id */ - } ASPI; - } API; - 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 */ -}; - -vmscond_t device_lookup( size_t devlen, char *devnam, int create, - struct DEV **retdev ); -void device_done( struct DEV *dev ); - -#endif /* # ifndef _DEVICE_H */ +/* Device.h Definitions for device 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#ifndef _DEVICE_H +#define _DEVICE_H + +#ifdef _WIN32 +#include /* HANDLE */ +#endif + +#include +#include +#include "access.h" +#include "cache.h" +#include "ods2.h" +#include "phyio.h" + +struct DEV { /* Device information */ + struct CACHE cache; + struct VCB *vcb; /* Pointer to volume (if mounted) */ + options_t 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) */ + unsigned blockspersector; /* Device physical sectorsize (blocks) */ + union { + struct { /* Device uses Win32 APIs for physical I/O */ + HANDLE handle; /* Win32 I/O handle */ + } Win32; + struct { /* Device uses ASPI APIs for physical I/O */ + short dtype; /* ASPI disk type */ + short bus; /* ASPI device bus */ + short id; /* ASPI device id */ + } ASPI; + } API; + 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 */ +}; + +vmscond_t device_lookup( size_t 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 index 30fe9e6..cb0fc94 100644 --- a/extracters/ods2/diffcmd.c +++ b/extracters/ods2/diffcmd.c @@ -1,154 +1,154 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#if !defined( DEBUG ) && defined( DEBUG_DIFFCMD ) -#define DEBUG DEBUG_DIFFCMD -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include "cmddef.h" -#include "phyio.h" - -/******************************************************************* dodiff() */ - -/* dodiff: a simple file difference routine */ - -static uint32_t maxdiffs; - -#define diff_maxdiff OPT_GENERIC_1 -#define diff_binary OPT_GENERIC_2 -qual_t -diffquals[] = {{"maximum_differences", diff_maxdiff, 0, DV(&maxdiffs), "commands difference qual_maxdiff"}, - {"nomaximum_differences", 0, diff_maxdiff, NV, NULL }, - {"binary", diff_binary, 0, NV, "commands difference qual_binary"}, - - { NULL, 0, 0, NV, NULL } -}; - -param_t -diffpars[] = { {"Files-11_filespec", REQ, FSPEC, NOPA, "commands difference Files-11_spec"}, - {"local_filespec", REQ, FSPEC, NOPA, "commands difference local_spec"}, - { NULL, 0, 0, NOPA, NULL } -}; - -DECL_CMD(diff) { - vmscond_t sts; - options_t options; - uint32_t records, diffs = 0; - char *rec, *cpy = NULL, *buf = NULL; - size_t bufsize = 80; - char *name; - FILE *tof; - struct FAB fab = cc$rms_fab; - struct RAB rab = cc$rms_rab; - struct NAM nam = cc$rms_nam; - char rsname[NAM$C_MAXRSS+1]; - - UNUSED(argc); - - if( $FAILS(sts = checkquals( &options, 0, diffquals, qualc, qualv )) ) - return sts; - - name = get_realpath( argv[2] ); - records = 0; - - nam.nam$l_rsa = rsname; - nam.nam$b_rss = (uint8_t)(sizeof( rsname) - 1); - - fab.fab$l_nam = &nam; - fab.fab$l_fna = argv[1]; - fab.fab$b_fns = (uint8_t)strlen( fab.fab$l_fna ); - - if ( (tof = openf( name? name: argv[2], "r" )) == NULL ) { - int err; - - err = errno; - printmsg( DIFF_OPENIN, 0, name? name: argv[2] ); - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, DIFF_OPENIN, strerror(err) ); - free( name ); - return sts; - } - - if( $SUCCESSFUL(sts = sys_open( &fab )) ) { - rab.rab$l_fab = &fab; - nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; - if( $SUCCESSFUL(sts = sys_connect( &rab )) ) { - if( (rec = malloc( MAXREC + 2 )) == NULL ) { - int err; - - err = errno; - sts = SS$_INSFMEM; - printmsg( sts, 0 ); - sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror(err) ); - } else { - rab.rab$l_ubf = rec; - rab.rab$w_usz = MAXREC; - while( $SUCCESSFUL(sts = sys_get( &rab )) ) { - rec[rab.rab$w_rsz] = '\0'; - ++records; - cpy = fgetline( tof, FALSE, &buf, &bufsize ); - if( cpy == NULL || - rab.rab$w_rsz != strlen( cpy ) || - memcmp( rec, cpy, rab.rab$w_rsz ) != 0 ) { - - if( options & diff_binary ) - sts = printmsg( DIFF_DIFFER, 0, records ); - else - printf( "************\n" - "File %s\n" - "%5u %.*s\n" - "******\n" - "File %s\n" - "%5u %s\n" - "************\n", - rsname, - records, rab.rab$w_rsz, rec, - name, - records, cpy? cpy: "" ); - if( (++diffs > maxdiffs && (options & diff_maxdiff)) || cpy == NULL ) - break; - } - cpy = NULL; - } - free( rec ); - rec = cpy = NULL; - } - sys_disconnect(&rab); - } - sys_close(&fab); - } else { - printmsg( DIFF_OPENIN, 0, argv[1] ); - sts = printmsg( sts, MSG_CONTINUE, DIFF_OPENIN ); - } - if( sts == RMS$_EOF ) { - if( (cpy = fgetline( tof, FALSE, &buf, &bufsize )) == NULL ) - sts = SS$_NORMAL; - else { - if( diffs < maxdiffs ) - sts = printmsg( DIFF_DIFFER, 0, records ); - } - } - - if( buf != NULL ) free( buf ); - fclose(tof); - - if( $SUCCESSFUL(sts) ) { - sts = printmsg( DIFF_COMPARED, 0, records ); - } else if( !$MATCHCOND(sts, DIFF_DIFFER) ) { - sts = printmsg( sts, 0, rab.rab$l_stv ); - } - free( name ); - return sts; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DIFFCMD ) +#define DEBUG DEBUG_DIFFCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" +#include "phyio.h" + +/******************************************************************* dodiff() */ + +/* dodiff: a simple file difference routine */ + +static uint32_t maxdiffs; + +#define diff_maxdiff OPT_GENERIC_1 +#define diff_binary OPT_GENERIC_2 +qual_t +diffquals[] = {{"maximum_differences", diff_maxdiff, 0, DV(&maxdiffs), "commands difference qual_maxdiff"}, + {"nomaximum_differences", 0, diff_maxdiff, NV, NULL }, + {"binary", diff_binary, 0, NV, "commands difference qual_binary"}, + + { NULL, 0, 0, NV, NULL } +}; + +param_t +diffpars[] = { {"Files-11_filespec", REQ, FSPEC, NOPA, "commands difference Files-11_spec"}, + {"local_filespec", REQ, FSPEC, NOPA, "commands difference local_spec"}, + { NULL, 0, 0, NOPA, NULL } +}; + +DECL_CMD(diff) { + vmscond_t sts; + options_t options; + uint32_t records, diffs = 0; + char *rec, *cpy = NULL, *buf = NULL; + size_t bufsize = 80; + char *name; + FILE *tof; + struct FAB fab = cc$rms_fab; + struct RAB rab = cc$rms_rab; + struct NAM nam = cc$rms_nam; + char rsname[NAM$C_MAXRSS+1]; + + UNUSED(argc); + + if( $FAILS(sts = checkquals( &options, 0, diffquals, qualc, qualv )) ) + return sts; + + name = get_realpath( argv[2] ); + records = 0; + + nam.nam$l_rsa = rsname; + nam.nam$b_rss = (uint8_t)(sizeof( rsname) - 1); + + fab.fab$l_nam = &nam; + fab.fab$l_fna = argv[1]; + fab.fab$b_fns = (uint8_t)strlen( fab.fab$l_fna ); + + if ( (tof = openf( name? name: argv[2], "r" )) == NULL ) { + int err; + + err = errno; + printmsg( DIFF_OPENIN, 0, name? name: argv[2] ); + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, DIFF_OPENIN, strerror(err) ); + free( name ); + return sts; + } + + if( $SUCCESSFUL(sts = sys_open( &fab )) ) { + rab.rab$l_fab = &fab; + nam.nam$l_rsa[nam.nam$b_rsl] = '\0'; + if( $SUCCESSFUL(sts = sys_connect( &rab )) ) { + if( (rec = malloc( MAXREC + 2 )) == NULL ) { + int err; + + err = errno; + sts = SS$_INSFMEM; + printmsg( sts, 0 ); + sts = printmsg( ODS2_OSERROR, MSG_CONTINUE, sts, strerror(err) ); + } else { + rab.rab$l_ubf = rec; + rab.rab$w_usz = MAXREC; + while( $SUCCESSFUL(sts = sys_get( &rab )) ) { + rec[rab.rab$w_rsz] = '\0'; + ++records; + cpy = fgetline( tof, FALSE, &buf, &bufsize ); + if( cpy == NULL || + rab.rab$w_rsz != strlen( cpy ) || + memcmp( rec, cpy, rab.rab$w_rsz ) != 0 ) { + + if( options & diff_binary ) + sts = printmsg( DIFF_DIFFER, 0, records ); + else + printf( "************\n" + "File %s\n" + "%5u %.*s\n" + "******\n" + "File %s\n" + "%5u %s\n" + "************\n", + rsname, + records, rab.rab$w_rsz, rec, + name, + records, cpy? cpy: "" ); + if( (++diffs > maxdiffs && (options & diff_maxdiff)) || cpy == NULL ) + break; + } + cpy = NULL; + } + free( rec ); + rec = cpy = NULL; + } + sys_disconnect(&rab); + } + sys_close(&fab); + } else { + printmsg( DIFF_OPENIN, 0, argv[1] ); + sts = printmsg( sts, MSG_CONTINUE, DIFF_OPENIN ); + } + if( sts == RMS$_EOF ) { + if( (cpy = fgetline( tof, FALSE, &buf, &bufsize )) == NULL ) + sts = SS$_NORMAL; + else { + if( diffs < maxdiffs ) + sts = printmsg( DIFF_DIFFER, 0, records ); + } + } + + if( buf != NULL ) free( buf ); + fclose(tof); + + if( $SUCCESSFUL(sts) ) { + sts = printmsg( DIFF_COMPARED, 0, records ); + } else if( !$MATCHCOND(sts, DIFF_DIFFER) ) { + sts = printmsg( sts, 0, rab.rab$l_stv ); + } + free( name ); + return sts; +} diff --git a/extracters/ods2/dircmd.c b/extracters/ods2/dircmd.c index 44e549e..fee66da 100644 --- a/extracters/ods2/dircmd.c +++ b/extracters/ods2/dircmd.c @@ -1,821 +1,821 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#if !defined( DEBUG ) && defined( DEBUG_DIRCMD ) -#define DEBUG DEBUG_DIRCMD -#else -#ifndef DEBUG -#define DEBUG 0 -#endif -#endif - -#include "cmddef.h" - -#include "f11def.h" - -struct dirstats { - uint32_t filecount; - uint32_t totblocks, totalloc, dircount; -}; - -static vmscond_t dir_1_arg( FILE *of, options_t options, struct dirstats *stats, - char *filespec ); -static void dirtotal( FILE *of, options_t options, uint32_t size, uint32_t alloc ); - -static vmscond_t get_fileheader( FILE *of, struct dsc$descriptor *devname, - struct fiddef *fid, uint32_t *clustersize, struct HEAD **buf ); -static void print_fileheader( FILE *of, struct HEAD *fhd, uint32_t *vbn, uint32_t clustersize ); - -/******************************************************************* dodir() */ - -#define dir_extra (dir_date | dir_fileid | dir_owner | dir_prot | dir_size) -#define dir_date OPT_SHARED_1 -#define dir_fileid OPT_SHARED_2 -#define dir_owner OPT_SHARED_3 -#define dir_prot OPT_SHARED_4 -#define dir_size OPT_SHARED_5 - -#define dir_grand OPT_SHARED_6 -#define dir_heading OPT_SHARED_7 -#define dir_names OPT_SHARED_8 -#define dir_trailing OPT_SHARED_9 -#define dir_full OPT_SHARED_10 -#define dir_total OPT_SHARED_11 - -#define dir_backup OPT_SHARED_12 -#define dir_created OPT_SHARED_13 -#define dir_expired OPT_SHARED_14 -#define dir_modified OPT_SHARED_15 -#define dir_dates (dir_backup | dir_created | dir_expired | dir_modified) - -#define dir_allocated OPT_SHARED_16 -#define dir_used OPT_SHARED_17 -#define dir_sizes (dir_allocated | dir_used) - -#define dir_mappings OPT_SHARED_19 -#define dir_pack OPT_SHARED_20 - -#define dir_default (dir_heading|dir_names|dir_trailing|dir_pack) - -const int dir_defaults = dir_default; -static char *outfile; - -static qual_t -datekwds[] = { {"created", dir_created, 0, NV, - "commands directory qual_date date_created"}, - {"modified", dir_modified, 0, NV, - "commands directory qual_date date_modified"}, - {"expired", dir_expired, 0, NV, - "commands directory qual_date date_expired"}, - {"backup", dir_backup, 0, NV, - "commands directory qual_date date_backup"}, - - {NULL, 0, 0, NV, NULL} -}; -static qual_t -sizekwds[] = { {"all", dir_used|dir_allocated, 0, NV, - "commands directory qual_size size_both" }, - {"allocation", dir_allocated, 0, NV, - "commands directory qual_size size_alloc" }, - {"used", dir_used, 0, NV, - "commands directory qual_size size_used" }, - - {NULL, 0, 0, NV, NULL} -}; -qual_t -dirquals[] = { {"brief", dir_default, ~dir_default, NV, - "commands directory qual_brief"}, - {"date", dir_date, dir_dates, VOPT(KV(datekwds)), - "-commands directory qual_date", }, - {"nodate", 0, dir_date, NV, NULL, }, - {"file_id", dir_fileid, 0, NV, - "-commands directory qual_fileid"}, - {"nofile_id", 0, dir_fileid, NV, NULL }, - {"full", dir_full|dir_heading|dir_trailing, - ~(dir_full|dir_pack), NV, - "commands directory qual_full"}, - {"grand_total", dir_grand, ~dir_grand & ~(dir_size|dir_sizes), - NV, "-commands directory qual_grand"}, - {"nogrand_total", 0, dir_grand, NV, NULL}, - {"heading", dir_heading, 0, NV, - "-commands directory qual_heading"}, - {"noheading", 0, dir_heading, NV, NULL}, - {"owner", dir_owner, 0, NV, - "-commands directory qual_owner"}, - {"noowner", 0, dir_owner, NV, NULL, }, - {"output", 0, 0, SV(&outfile), - "commands directory qual_output"}, - {"pack", dir_pack, 0, NV, - "-commands directory qual_pack"}, - {"nopack", dir_full, dir_pack|dir_heading|dir_trailing|dir_grand, - NV, NULL, }, - {"protection", dir_prot, 0, NV, - "-commands directory qual_protection"}, - {"noprotection", 0, dir_prot, NV, NULL, }, - {"size", dir_size, dir_sizes, VOPT(KV(sizekwds)), - "-commands directory qual_size"}, - {"nosize", 0, dir_size|dir_sizes, - NV, NULL, }, - {"total", dir_total|dir_heading, - ~dir_total & ~(dir_size|dir_sizes), - NV, - "commands directory qual_total",}, - {"trailing", dir_trailing, 0, NV, - "-commands directory qual_trailing",}, - {"notrailing", 0, dir_trailing, NV, NULL}, - {"map_area", dir_mappings|dir_full|dir_heading|dir_trailing, - 0, NV, "-commands directory qual_map"}, - {"nomap_area", 0, dir_mappings, NV, NULL}, - - {NULL, 0, 0, NV, NULL} }; - -defqualsp_t dir_defopt = NULL; - -param_t -dirpars[] = { {"filespec", OPT | NOLIM, FSPEC, NOPA, "commands directory filespec"}, - - { NULL, 0, 0, NOPA, NULL } -}; - - -/************************************************************ dodir() */ - -DECL_CMD(dir) { - FILE *of = stdout; - options_t options; - vmscond_t status; - struct dirstats stats; - - memset( &stats, 0, sizeof( stats ) ); - outfile = NULL; - - if( dir_defopt != NULL ) { - if( $FAILS(status = checkquals( &options, dir_defaults, dirquals, - dir_defopt->qualc, dir_defopt->qualv )) ) { - return status; - } - } - else - options = dir_defaults; - - if( $FAILS(status = checkquals( &options, options, dirquals, qualc, qualv )) ) { - return status; - } - - if( outfile != NULL ) { - of = openf( outfile, "w" ); - if( of == NULL ) { - int err; - - err = errno; - printmsg( DIRECT_OPENOUT, 0, outfile ); - return printmsg( ODS2_OSERROR, MSG_CONTINUE, DIRECT_OPENOUT, - strerror( err ) ); - } - } - - 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; - - --argc; - ++argv; - do { - status = dir_1_arg( of, options, &stats, argv++[0] ); - } while( argc && --argc ); - - if( (options & dir_grand) || - (stats.dircount > 1 && (options & dir_trailing)) ) { - printmsg( DIRECT_GRANDTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, stats.dircount, stats.filecount ); - dirtotal( of, options, stats.totblocks, stats.totalloc ); - fputs( ".\n", of ); - } - if( options & dir_pack ) { - if( $SUCCESSFUL( status ) ) { - if( stats.filecount < 1 ) - status = printmsg( DIRECT_NOFILES, MSG_TOFILE, of ); - } else { - status = printmsg( status, MSG_TOFILE, of ); - } - } - - if( outfile != NULL ) - fclose( of ); - return status; -} - -/***********************************************************dir_1_arg() */ - -static vmscond_t dir_1_arg( FILE *of, options_t options, struct dirstats *stats, - char *filespec ) { - char res[NAM$C_MAXRSS + 1] = { "" }, rsa[NAM$C_MAXRSS + 1] = { "" }; - vmscond_t status; - uint32_t nobackup = 0, contigb = 0, contig = 0, directory = 0, - writeback = 0, readcheck = 0, writecheck = 0, locked = 0, spool = 0, - badblock = 0, markdel = 0; - uint16_t retlen = 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(nobackup), NULL, 0 }, - { XAB$_UCHAR_CONTIG, sizeof(contig), NULL, 0 }, - { XAB$_UCHAR_CONTIGB, sizeof(contigb), NULL, 0 }, - { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, - { XAB$_UCHAR_WRITEBACK, sizeof(writeback), NULL, 0 }, - { XAB$_UCHAR_READCHECK, sizeof(readcheck), NULL, 0 }, - { XAB$_UCHAR_WRITECHECK, sizeof(writecheck), NULL, 0 }, - { XAB$_UCHAR_LOCKED, sizeof(locked), NULL, 0 }, - { XAB$_UCHAR_SPOOL, sizeof(spool), NULL, 0 }, - { XAB$_UCHAR_MARKDEL, sizeof(markdel), NULL, 0 }, - { XAB$_UCHAR_BADBLOCK, sizeof(badblock), NULL, 0 }, - { 0, 0, NULL, 0 } - }; - - 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[0].retlen = &retlen; - xitems[1].buffer = &contig; xitems[1].retlen = &retlen; - xitems[2].buffer = &contigb; xitems[2].retlen = &retlen; - xitems[3].buffer = &directory; xitems[3].retlen = &retlen; - xitems[4].buffer = &writeback; xitems[4].retlen = &retlen; - xitems[5].buffer = &readcheck; xitems[5].retlen = &retlen; - xitems[6].buffer = &writecheck; xitems[6].retlen = &retlen; - xitems[7].buffer = &locked; xitems[7].retlen = &retlen; - xitems[8].buffer = &spool; xitems[8].retlen = &retlen; - xitems[9].buffer = &markdel; xitems[9].retlen = &retlen; - xitems[10].buffer = &badblock; xitems[10].retlen = &retlen; - - itm.xab$b_mode = XAB$K_SENSEMODE; - itm.xab$l_itemlist = xitems; - - fab.fab$l_fna = filespec; - fab.fab$b_fns = filespec? (uint8_t)strlen(fab.fab$l_fna): 0; - fab.fab$l_dna = "*.*;*"; - fab.fab$b_dns = (uint8_t)strlen(fab.fab$l_dna); - - if( $SUCCESSFUL(status = sys_parse(&fab)) ) { - char dir[NAM$C_MAXRSS + 1] = { "" }; - size_t namelen; - size_t dirlen = 0; - uint32_t dirfiles = 0; - uint32_t dirblocks = 0, diralloc = 0; - size_t printcol = 0; - - nam.nam$l_rsa = rsa; - nam.nam$b_rss = NAM$C_MAXRSS; - fab.fab$l_fop = 0; - while( $SUCCESSFUL(status) && $SUCCESSFUL(status = sys_search( &fab )) ) { - - if (dirlen != (size_t)nam.nam$b_dev + nam.nam$b_dir || - memcmp(rsa, dir, (size_t)nam.nam$b_dev + nam.nam$b_dir) != 0) { - if (dirfiles > 0 && (options & dir_trailing)) { - if (printcol > 0) - fputc( '\n', of ); - printmsg( DIRECT_FILETOTAL, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dirfiles ); - dirtotal( of, options, dirblocks, diralloc ); - fputs(".\n", of); - } - dirlen = (size_t)nam.nam$b_dev + nam.nam$b_dir; - memcpy(dir, rsa, dirlen); - dir[dirlen] = '\0'; - if( options & dir_heading) - printmsg( DIRECT_DIRHEAD, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dir ); - stats->filecount += dirfiles; - stats->totblocks += dirblocks; - stats->totalloc += diralloc; - stats->dircount++; - dirfiles = 0; - dirblocks = 0; - diralloc = 0; - printcol = 0; - } - rsa[nam.nam$b_rsl] = '\0'; - namelen = (size_t)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) { - size_t newcol = (printcol + 20) / 20 * 20; - if (newcol + namelen >= 80) { - fputs( "\n", of ); - printcol = 0; - } else { - fprintf( of, "%*s", (int)(newcol - printcol), " " ); - printcol = newcol; - } - } - fputs( rsa + dirlen, of ); - printcol += namelen; - } - } else { - if (options & dir_names) { - if ( (options & dir_heading) == 0 ) - fprintf( of, "%s", dir ); - if( options & dir_pack ) { - if( namelen > 18 ) { - fprintf( of, "%s", rsa + dirlen ); - if( options & dir_extra ) - fprintf( of, "\n " ); - } else { - fprintf( of, "%-19s", rsa + dirlen ); - } - } else - fprintf( of, "%s\n", rsa + dirlen ); - } - if( $FAILS(status = sys_open(&fab)) ) { - printmsg( status, MSG_TOFILE, of ); - status = SS$_NORMAL; - } else { - status = 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) - fprintf( of, "%sFile ID:", (options & dir_pack)? " ": "" ); - (void) snprintf(fileid, sizeof(fileid), "(%u,%u,%u)", - (nam.nam$w_fid.fid$b_nmx << 16) | nam.nam$w_fid.fid$w_num, - nam.nam$w_fid.fid$w_seq, nam.nam$w_fid.fid$b_rvn ); - fprintf( of, " %-22s", fileid ); - } - diralloc += fab.fab$l_alq; - if (options & dir_used) { - uint32_t filesize; - - filesize = fhc.xab$l_ebk; /* VBN of eof */ - if( filesize == 0 ) /* none, use allocated size */ - filesize = fab.fab$l_alq; - else { - 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) - printmsg( DIRECT_FULLSIZE, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, filesize ); - else - fprintf( of, "%9u", filesize ); - if( options & (dir_allocated|dir_full)) - fprintf( of, "/%-9u", fab.fab$l_alq ); - if( options & (dir_allocated | dir_full) ) { - if( options & dir_pack ) - fputs( " ", of ); - else - fputc( '\n', of ); - } - } - } else { - if ( (options & (dir_allocated|dir_names)) == (dir_allocated|dir_names)) { - fprintf( of, "%9u", fab.fab$l_alq ); - } - } -#define pprot(val, pos, del) do { \ - unsigned int v = ~(((val) >> (pos)) & xab$m_prot); \ - if( v & xab$m_noread ) fprintf( of, "R" ); \ - if( v & xab$m_nowrite ) fprintf( of, "W" ); \ - if( v & xab$m_noexe ) fprintf( of, "E" ); \ - if( v & xab$m_nodel ) fprintf( of, "D" ); \ - fprintf( of, del ); \ - } while( 0 ) - - if (options & dir_full) { - int pos = 0; - - printmsg( DIRECT_OWNER, MSG_TEXT|MSG_TOFILE, of, - ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF); - printmsg( DIRECT_CREATED, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); - prvmstime( of, dat.xab$q_cdt, "\n" ); - printmsg( DIRECT_REVISED, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); - prvmstime( of, dat.xab$q_rdt, " (" ); fprintf( of, "%u)\n", dat.xab$w_rvn ); - printmsg( DIRECT_EXPIRES, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); - prvmstime( of, dat.xab$q_edt, "\n" ); - printmsg( DIRECT_BACKUP, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); - prvmstime( of, dat.xab$q_bdt, "\n" ); - -#define MSG(x) getmsg( DIRECT_ ## x, MSG_TEXT|MSG_NOCRLF ) - pwrap( of, &pos, MSG( FILEORG ) ); - switch( fab.fab$b_org ) { - case FAB$C_SEQ: - pwrap( of, &pos, MSG( SEQORG) ); break; - case FAB$C_REL: - pwrap( of, &pos, MSG( RELORG ) /*, Maximum record number %u", fab.fab$l_mrn*/ ); break; - case FAB$C_IDX: - pwrap( of, &pos, MSG( IDXORG ) ); break; /*, Prolog: 3, Using 4 keys\nIn 3 areas */ - default: - pwrap( of, &pos, MSG( UNKORG ), fab.fab$b_org ); break; - } - - pwrap( of, &pos, MSG( FILEATT ) ); - pwrap( of, &pos, MSG( ALLOC ), fab.fab$l_alq ); - pwrap( of, &pos, MSG( EXTEND ), fab.fab$w_deq ); - /* Missing: , Maximum bucket size: n*/ - pwrap( of, &pos, MSG( GBC ), fab.fab$w_gbc ); - - if( fhc.xab$w_verlimit == 0 || fhc.xab$w_verlimit == 32767 ) { - if( directory ) - pwrap( of, &pos, MSG(NODIRLIMIT) ); - else - pwrap( of, &pos, MSG(NOVERLIMIT ) ); - } else { - if( directory ) - pwrap( of, &pos, MSG(DIRLIMIT), fhc.xab$w_verlimit ); - else - pwrap( of, &pos, MSG(VERLIMIT), fhc.xab$w_verlimit ); - } -#define pattr(bit, text) \ - if( bit ) \ - pwrap( of, &pos, MSG(text) ) - pattr( contig, CONTIG ); - pattr( contigb, CONTIGB ); - pattr( nobackup, NOBACKUP ); - pattr( directory, DIRECTORY ); - pattr( writeback, WRITEBACK ); - pattr( readcheck, READCHECK ); - pattr( writecheck, WRITECHECK ); - pattr( locked, LOCKED ); - pattr( spool, SPOOL ); - pattr( markdel, MARKDEL ); - pattr( badblock, BADBLOCK ); - pwrap( of, &pos, "\n" ); -#undef pattr - pwrap( of, &pos, MSG(EOFPOS), fhc.xab$l_ebk, fhc.xab$w_ffb ); - - pwrap( of, &pos, MSG(RECFMT) ); - switch( fab.fab$b_rfm ) { - default: - case FAB$C_UDF: - pwrap( of, &pos, MSG(RECUDF) ); break; - case FAB$C_FIX: - pwrap( of, &pos, MSG(RECFIX), fab.fab$w_mrs ); break; - case FAB$C_VAR: - pwrap( of, &pos, MSG(RECVAR), fhc.xab$w_lrl ); break; - case FAB$C_VFC: - pwrap( of, &pos, MSG(RECVFC), - (fab.fab$b_fsz? fab.fab$b_fsz: 2), - fhc.xab$w_lrl ); break; - case FAB$C_STM: - pwrap( of, &pos, MSG(RECSTM) ); break; - case FAB$C_STMLF: - pwrap( of, &pos, MSG(RECSTMLF) ); break; - case FAB$C_STMCR: - pwrap( of, &pos, MSG(RECSTMCR) ); break; - } - pwrap( of, &pos, "\n" ); - - pwrap( of, &pos, MSG(RECATT) ); - if( fab.fab$b_rat == 0 ) - pwrap( of, &pos, MSG(RECATT_NONE) ); - else { - const char *more = ""; - if( fab.fab$b_rat & FAB$M_FTN ) { - pwrap( of, &pos, MSG(RECATT_FTN), more ); - more = ", "; - } - if( fab.fab$b_rat & FAB$M_CR ) { - pwrap( of, &pos, MSG(RECATT_CR), more ); - more = ", "; - } - if( fab.fab$b_rat & FAB$M_PRN ) { - pwrap( of, &pos, MSG(RECATT_PRN), more ); - more = ", "; - } - if( fab.fab$b_rat & FAB$M_BLK ) { - pwrap( of, &pos, MSG(RECATT_BLK), more ); - } - } - fprintf( of, "\n" ); - /* - * RMS attributes: None - * Journaling enabled: None - */ - fprintf( of, MSG(PROT_SYS) ); - pprot(pro.xab$w_pro, xab$v_system, MSG(PROT_OWNER) ); - pprot(pro.xab$w_pro, xab$v_owner, MSG(PROT_GROUP) ); - pprot(pro.xab$w_pro, xab$v_group, MSG(PROT_WORLD) ); - pprot(pro.xab$w_pro, xab$v_world, "\n"); - - if( options & dir_mappings ) { - struct dsc$descriptor devnam; - unsigned clustersize = 0; - struct HEAD *buf = NULL; - struct fiddef lastfid, fid; - unsigned vbn = 1; - - memset( &devnam, 0, sizeof( devnam ) ); - devnam.dsc$b_dtype = DSC$K_DTYPE_T; - devnam.dsc$b_class = DSC$K_CLASS_S; - devnam.dsc$w_length = nam.nam$b_dev; - devnam.dsc$a_pointer = nam.nam$l_dev; - - fid = nam.nam$w_fid; - do { - if( $SUCCESSFUL(status = get_fileheader( of, - &devnam, - &fid, - &clustersize, - &buf )) ) { - print_fileheader( of, buf, &vbn, clustersize ); - } - lastfid = fid; - fid = buf->fh2$w_ext_fid; - free( buf ); - buf = NULL; - } while( $SUCCESSFUL(status) && - (fid.fid$w_num | fid.fid$w_seq | fid.fid$b_rvn | - fid.fid$b_nmx) != 0 && - memcmp( &fid, &lastfid, sizeof( fid ) ) != 0 ); - } - } else { /* !full */ - if (options & dir_date) { - if( options & dir_created ) - status = prvmstime( of, dat.xab$q_cdt, NULL ); - if( options & dir_modified ) - status = prvmstime( of, dat.xab$q_rdt, NULL ); - if( options & dir_expired ) - status = prvmstime( of, dat.xab$q_edt, NULL ); - if( options & dir_backup ) - status = prvmstime( of, dat.xab$q_bdt, NULL ); - } - if (options & dir_owner) { - printmsg( DIRECT_UIC, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, - ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF ); - } - if (options & dir_prot) { - fprintf( of, " (" ); - 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) - fprintf( of, "\n" ); - } - } - dirfiles++; - } - if( status == RMS$_NMF ) - status = SS$_NORMAL; - if( printcol > 0 ) fprintf( of, "\n" ); - if (options & dir_trailing) { - printmsg( DIRECT_FILETOTAL, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dirfiles ); - dirtotal( of, options, dirblocks, diralloc ); - fputs( ".\n", of ); - } - stats->filecount += dirfiles; - stats->totblocks += dirblocks; - stats->totalloc += diralloc; - } - - fab.fab$b_fns = - nam.nam$b_ess = - fab.fab$b_dns = 0; - nam.nam$b_nop = NAM$M_SYNCHK; - (void) sys_parse( &fab ); - - return status; -} - -/*********************************************************** dirtotal() */ - -static void dirtotal( FILE *of, options_t options, uint32_t size, uint32_t alloc ) { - if ( !(options & dir_size) ) - return; - fputs( ", ", of ); - - switch( options & (dir_used | dir_allocated) ) { - case dir_used: - printmsg( DIRECT_USEDTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, size ); - break; - case dir_allocated: - printmsg( DIRECT_ALLOCTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, alloc ); - break; - case dir_used | dir_allocated: - printmsg( DIRECT_BOTHSIZE, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, size, alloc ); - break; - default: - return; - } -} - -/*********************************************************** get_fileheader() */ -static unsigned get_fileheader( FILE *of, struct dsc$descriptor *devname, - struct fiddef *fid, unsigned *clustersize, - struct HEAD **buf ) { - unsigned status; - char *idxbuf; - struct HOME *hom; - long vbn; - struct FAB idxfab = cc$rms_fab; - struct RAB idxrab = cc$rms_rab; - struct NAM idxnam = cc$rms_nam; - char rsbuf[NAM$C_MAXRSS + 1] = { "" }; - - if( devname->dsc$w_length > NAM$S_DVI ) - return RMS$_FNM; - - idxbuf = (char *)malloc( 512 ); - *buf = (struct HEAD *) idxbuf; - if( idxbuf == NULL ) - return SS$_INSFMEM; - - idxfab.fab$l_fna = NULL; - idxfab.fab$b_fns = 0; - idxfab.fab$l_dna = NULL; - idxfab.fab$b_dns = 0; - idxfab.fab$b_fac = FAB$M_GET; - - idxfab.fab$l_fop = FAB$M_NAM; - idxnam.nam$t_dvi[0] = (char)devname->dsc$w_length; - memcpy( idxnam.nam$t_dvi + 1, devname->dsc$a_pointer, devname->dsc$w_length ); - - idxnam.nam$w_did.fid$w_num = 4; - idxnam.nam$w_did.fid$w_seq = 4; - idxnam.nam$w_did.fid$b_nmx = 0; - idxnam.nam$w_did.fid$b_rvn = fid->fid$b_rvn; - - idxnam.nam$w_fid.fid$w_num = 1; - idxnam.nam$w_fid.fid$w_seq = 1; - idxnam.nam$w_fid.fid$b_nmx = 0; - idxnam.nam$w_fid.fid$b_rvn = fid->fid$b_rvn; - idxnam.nam$l_rsa = rsbuf; - idxnam.nam$b_rss = NAM$C_MAXRSS; - - do { - idxfab.fab$l_nam = &idxnam; - if( $FAILS(status = sys_open(&idxfab)) ) - break; - - idxrab.rab$l_fab = &idxfab; - if( $FAILS(status = sys_connect( &idxrab )) ) { - sys_close( &idxfab ); - break; - } - - idxrab.rab$l_ubf = idxbuf; - idxrab.rab$w_usz = 512; - idxrab.rab$w_rfa[2] = 0; - idxrab.rab$w_rfa[1] = (uint16_t) (2 >> 16); /* HOMVBN */ - idxrab.rab$w_rfa[0] = (uint16_t) (2); - - if( $FAILS(status = sys_get( &idxrab )) ) - break; - - hom = (struct HOME *)idxbuf; - vbn = ( F11WORD( hom->hm2$w_ibmapvbn ) + - F11WORD( hom->hm2$w_ibmapsize ) + - ((fid->fid$b_nmx << 16) | - fid->fid$w_num) -1 ); - *clustersize = F11WORD( hom->hm2$w_cluster ); - - idxrab.rab$w_rfa[2] = 0; - idxrab.rab$w_rfa[1] = (uint16_t) (vbn >> 16); - idxrab.rab$w_rfa[0] = (uint16_t) (vbn); - - status = sys_get( &idxrab ); - } while( 0 ); - - sys_disconnect( &idxrab ); - sys_close( &idxfab ); - if( $FAILED(status) ) { - printmsg( ODS2_FILHDRACC, 0 ); - status = printmsg( status, MSG_CONTINUE|MSG_NOARGS|MSG_TOFILE, of, ODS2_FILHDRACC ); - } - return status; -} - -/*********************************************************** print_fileheader() */ - -static void print_fileheader( FILE *of, struct HEAD *fhd, uint32_t *vbn, - uint32_t clustersize ) { - f11word *mp, *ep; - f11long hiblk; - struct IDENT *id; - size_t idsize; - char fname[ sizeof( id->fi2$t_filename ) + sizeof( id->fi2$t_filenamext ) + 1 ]; - - id = (struct IDENT *)(((f11word *)fhd) + fhd->fh2$b_idoffset); - idsize = (size_t)(fhd->fh2$b_mpoffset - fhd->fh2$b_idoffset) * 2; - - if( idsize >= sizeof( id->fi2$t_filename ) ) { - char *p; - - memset( fname, ' ', sizeof( fname ) ); - memcpy( fname, id->fi2$t_filename, sizeof( id->fi2$t_filename )); - idsize -= offsetof( struct IDENT, fi2$t_filenamext ); - if( idsize ) { - if( idsize > sizeof( id->fi2$t_filenamext ) ) - idsize = sizeof( id->fi2$t_filenamext ); - memcpy( fname + sizeof( id->fi2$t_filename ), id->fi2$t_filenamext, idsize ); - } - for( p = fname + sizeof(fname); p > fname && p[-1] == ' '; --p ) - p[-1] = '\0'; - fname[sizeof(fname) -1] = '\0'; - printmsg( DIRECT_IDNAME, MSG_TEXT|MSG_TOFILE, of, fname ); - } - - hiblk = F11SWAP( fhd->fh2$w_recattr.fat$l_hiblk ); - - if( fhd->fh2$w_seg_num != 0 ) - printmsg( DIRECT_EXTNHDR, MSG_TEXT|MSG_TOFILE, of, - F11WORD( fhd->fh2$w_seg_num ), - (((uint32_t)fhd->fh2$w_ext_fid.fid$b_nmx) << 16 | - F11WORD(fhd->fh2$w_ext_fid.fid$w_num)), - F11WORD(fhd->fh2$w_ext_fid.fid$w_seq), - fhd->fh2$w_ext_fid.fid$b_rvn ); - - printmsg( DIRECT_HDROFFSETS, MSG_TEXT|MSG_TOFILE, of, fhd->fh2$b_idoffset, - fhd->fh2$b_mpoffset, fhd->fh2$b_acoffset, fhd->fh2$b_rsoffset ); - printmsg( DIRECT_MAPSIZE, MSG_TEXT|MSG_TOFILE, of, - fhd->fh2$b_acoffset - fhd->fh2$b_mpoffset, - fhd->fh2$b_map_inuse, clustersize, hiblk ); - - if( fhd->fh2$b_idoffset >= 40 && fhd->fh2$l_highwater ) - printmsg( DIRECT_MAPHIGHWATER, MSG_TEXT|MSG_TOFILE, of, - F11LONG( fhd->fh2$l_highwater ) - 1 ); - - printmsg( DIRECT_STRUCLEVEL, MSG_TEXT|MSG_TOFILE, of, - fhd->fh2$w_struclev >> 8, fhd->fh2$w_struclev & 0xFFu ); - - mp = ((f11word *)fhd) + fhd->fh2$b_mpoffset; - ep = mp + fhd->fh2$b_map_inuse; - - if( mp >= ep ) - return; - - printmsg( DIRECT_MAPHEADER, MSG_TEXT|MSG_TOFILE, of ); - - while( mp < ep ) { - f11word e0; - uint32_t 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: - printmsg( DIRECT_MAPFMT0, MSG_TEXT|MSG_TOFILE, of, e0 ); - continue; - } - ++count; - printmsg( DIRECT_MAPENTRY, MSG_TEXT|MSG_TOFILE, of, *vbn, - (e0 >> 14), count, - pbn, pbn + count -1 ); - *vbn += count; - } - printmsg( DIRECT_MAPFREE, MSG_TEXT|MSG_TOFILE, of, *vbn ); - return; -} - +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#if !defined( DEBUG ) && defined( DEBUG_DIRCMD ) +#define DEBUG DEBUG_DIRCMD +#else +#ifndef DEBUG +#define DEBUG 0 +#endif +#endif + +#include "cmddef.h" + +#include "f11def.h" + +struct dirstats { + uint32_t filecount; + uint32_t totblocks, totalloc, dircount; +}; + +static vmscond_t dir_1_arg( FILE *of, options_t options, struct dirstats *stats, + char *filespec ); +static void dirtotal( FILE *of, options_t options, uint32_t size, uint32_t alloc ); + +static vmscond_t get_fileheader( FILE *of, struct dsc$descriptor *devname, + struct fiddef *fid, uint32_t *clustersize, struct HEAD **buf ); +static void print_fileheader( FILE *of, struct HEAD *fhd, uint32_t *vbn, uint32_t clustersize ); + +/******************************************************************* dodir() */ + +#define dir_extra (dir_date | dir_fileid | dir_owner | dir_prot | dir_size) +#define dir_date OPT_SHARED_1 +#define dir_fileid OPT_SHARED_2 +#define dir_owner OPT_SHARED_3 +#define dir_prot OPT_SHARED_4 +#define dir_size OPT_SHARED_5 + +#define dir_grand OPT_SHARED_6 +#define dir_heading OPT_SHARED_7 +#define dir_names OPT_SHARED_8 +#define dir_trailing OPT_SHARED_9 +#define dir_full OPT_SHARED_10 +#define dir_total OPT_SHARED_11 + +#define dir_backup OPT_SHARED_12 +#define dir_created OPT_SHARED_13 +#define dir_expired OPT_SHARED_14 +#define dir_modified OPT_SHARED_15 +#define dir_dates (dir_backup | dir_created | dir_expired | dir_modified) + +#define dir_allocated OPT_SHARED_16 +#define dir_used OPT_SHARED_17 +#define dir_sizes (dir_allocated | dir_used) + +#define dir_mappings OPT_SHARED_19 +#define dir_pack OPT_SHARED_20 + +#define dir_default (dir_heading|dir_names|dir_trailing|dir_pack) + +const int dir_defaults = dir_default; +static char *outfile; + +static qual_t +datekwds[] = { {"created", dir_created, 0, NV, + "commands directory qual_date date_created"}, + {"modified", dir_modified, 0, NV, + "commands directory qual_date date_modified"}, + {"expired", dir_expired, 0, NV, + "commands directory qual_date date_expired"}, + {"backup", dir_backup, 0, NV, + "commands directory qual_date date_backup"}, + + {NULL, 0, 0, NV, NULL} +}; +static qual_t +sizekwds[] = { {"all", dir_used|dir_allocated, 0, NV, + "commands directory qual_size size_both" }, + {"allocation", dir_allocated, 0, NV, + "commands directory qual_size size_alloc" }, + {"used", dir_used, 0, NV, + "commands directory qual_size size_used" }, + + {NULL, 0, 0, NV, NULL} +}; +qual_t +dirquals[] = { {"brief", dir_default, ~dir_default, NV, + "commands directory qual_brief"}, + {"date", dir_date, dir_dates, VOPT(KV(datekwds)), + "-commands directory qual_date", }, + {"nodate", 0, dir_date, NV, NULL, }, + {"file_id", dir_fileid, 0, NV, + "-commands directory qual_fileid"}, + {"nofile_id", 0, dir_fileid, NV, NULL }, + {"full", dir_full|dir_heading|dir_trailing, + ~(dir_full|dir_pack), NV, + "commands directory qual_full"}, + {"grand_total", dir_grand, ~dir_grand & ~(dir_size|dir_sizes), + NV, "-commands directory qual_grand"}, + {"nogrand_total", 0, dir_grand, NV, NULL}, + {"heading", dir_heading, 0, NV, + "-commands directory qual_heading"}, + {"noheading", 0, dir_heading, NV, NULL}, + {"owner", dir_owner, 0, NV, + "-commands directory qual_owner"}, + {"noowner", 0, dir_owner, NV, NULL, }, + {"output", 0, 0, SV(&outfile), + "commands directory qual_output"}, + {"pack", dir_pack, 0, NV, + "-commands directory qual_pack"}, + {"nopack", dir_full, dir_pack|dir_heading|dir_trailing|dir_grand, + NV, NULL, }, + {"protection", dir_prot, 0, NV, + "-commands directory qual_protection"}, + {"noprotection", 0, dir_prot, NV, NULL, }, + {"size", dir_size, dir_sizes, VOPT(KV(sizekwds)), + "-commands directory qual_size"}, + {"nosize", 0, dir_size|dir_sizes, + NV, NULL, }, + {"total", dir_total|dir_heading, + ~dir_total & ~(dir_size|dir_sizes), + NV, + "commands directory qual_total",}, + {"trailing", dir_trailing, 0, NV, + "-commands directory qual_trailing",}, + {"notrailing", 0, dir_trailing, NV, NULL}, + {"map_area", dir_mappings|dir_full|dir_heading|dir_trailing, + 0, NV, "-commands directory qual_map"}, + {"nomap_area", 0, dir_mappings, NV, NULL}, + + {NULL, 0, 0, NV, NULL} }; + +defqualsp_t dir_defopt = NULL; + +param_t +dirpars[] = { {"filespec", OPT | NOLIM, FSPEC, NOPA, "commands directory filespec"}, + + { NULL, 0, 0, NOPA, NULL } +}; + + +/************************************************************ dodir() */ + +DECL_CMD(dir) { + FILE *of = stdout; + options_t options; + vmscond_t status; + struct dirstats stats; + + memset( &stats, 0, sizeof( stats ) ); + outfile = NULL; + + if( dir_defopt != NULL ) { + if( $FAILS(status = checkquals( &options, dir_defaults, dirquals, + dir_defopt->qualc, dir_defopt->qualv )) ) { + return status; + } + } + else + options = dir_defaults; + + if( $FAILS(status = checkquals( &options, options, dirquals, qualc, qualv )) ) { + return status; + } + + if( outfile != NULL ) { + of = openf( outfile, "w" ); + if( of == NULL ) { + int err; + + err = errno; + printmsg( DIRECT_OPENOUT, 0, outfile ); + return printmsg( ODS2_OSERROR, MSG_CONTINUE, DIRECT_OPENOUT, + strerror( err ) ); + } + } + + 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; + + --argc; + ++argv; + do { + status = dir_1_arg( of, options, &stats, argv++[0] ); + } while( argc && --argc ); + + if( (options & dir_grand) || + (stats.dircount > 1 && (options & dir_trailing)) ) { + printmsg( DIRECT_GRANDTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, stats.dircount, stats.filecount ); + dirtotal( of, options, stats.totblocks, stats.totalloc ); + fputs( ".\n", of ); + } + if( options & dir_pack ) { + if( $SUCCESSFUL( status ) ) { + if( stats.filecount < 1 ) + status = printmsg( DIRECT_NOFILES, MSG_TOFILE, of ); + } else { + status = printmsg( status, MSG_TOFILE, of ); + } + } + + if( outfile != NULL ) + fclose( of ); + return status; +} + +/***********************************************************dir_1_arg() */ + +static vmscond_t dir_1_arg( FILE *of, options_t options, struct dirstats *stats, + char *filespec ) { + char res[NAM$C_MAXRSS + 1] = { "" }, rsa[NAM$C_MAXRSS + 1] = { "" }; + vmscond_t status; + uint32_t nobackup = 0, contigb = 0, contig = 0, directory = 0, + writeback = 0, readcheck = 0, writecheck = 0, locked = 0, spool = 0, + badblock = 0, markdel = 0; + uint16_t retlen = 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(nobackup), NULL, 0 }, + { XAB$_UCHAR_CONTIG, sizeof(contig), NULL, 0 }, + { XAB$_UCHAR_CONTIGB, sizeof(contigb), NULL, 0 }, + { XAB$_UCHAR_DIRECTORY, sizeof(directory), NULL, 0 }, + { XAB$_UCHAR_WRITEBACK, sizeof(writeback), NULL, 0 }, + { XAB$_UCHAR_READCHECK, sizeof(readcheck), NULL, 0 }, + { XAB$_UCHAR_WRITECHECK, sizeof(writecheck), NULL, 0 }, + { XAB$_UCHAR_LOCKED, sizeof(locked), NULL, 0 }, + { XAB$_UCHAR_SPOOL, sizeof(spool), NULL, 0 }, + { XAB$_UCHAR_MARKDEL, sizeof(markdel), NULL, 0 }, + { XAB$_UCHAR_BADBLOCK, sizeof(badblock), NULL, 0 }, + { 0, 0, NULL, 0 } + }; + + 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[0].retlen = &retlen; + xitems[1].buffer = &contig; xitems[1].retlen = &retlen; + xitems[2].buffer = &contigb; xitems[2].retlen = &retlen; + xitems[3].buffer = &directory; xitems[3].retlen = &retlen; + xitems[4].buffer = &writeback; xitems[4].retlen = &retlen; + xitems[5].buffer = &readcheck; xitems[5].retlen = &retlen; + xitems[6].buffer = &writecheck; xitems[6].retlen = &retlen; + xitems[7].buffer = &locked; xitems[7].retlen = &retlen; + xitems[8].buffer = &spool; xitems[8].retlen = &retlen; + xitems[9].buffer = &markdel; xitems[9].retlen = &retlen; + xitems[10].buffer = &badblock; xitems[10].retlen = &retlen; + + itm.xab$b_mode = XAB$K_SENSEMODE; + itm.xab$l_itemlist = xitems; + + fab.fab$l_fna = filespec; + fab.fab$b_fns = filespec? (uint8_t)strlen(fab.fab$l_fna): 0; + fab.fab$l_dna = "*.*;*"; + fab.fab$b_dns = (uint8_t)strlen(fab.fab$l_dna); + + if( $SUCCESSFUL(status = sys_parse(&fab)) ) { + char dir[NAM$C_MAXRSS + 1] = { "" }; + size_t namelen; + size_t dirlen = 0; + uint32_t dirfiles = 0; + uint32_t dirblocks = 0, diralloc = 0; + size_t printcol = 0; + + nam.nam$l_rsa = rsa; + nam.nam$b_rss = NAM$C_MAXRSS; + fab.fab$l_fop = 0; + while( $SUCCESSFUL(status) && $SUCCESSFUL(status = sys_search( &fab )) ) { + + if (dirlen != (size_t)nam.nam$b_dev + nam.nam$b_dir || + memcmp(rsa, dir, (size_t)nam.nam$b_dev + nam.nam$b_dir) != 0) { + if (dirfiles > 0 && (options & dir_trailing)) { + if (printcol > 0) + fputc( '\n', of ); + printmsg( DIRECT_FILETOTAL, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dirfiles ); + dirtotal( of, options, dirblocks, diralloc ); + fputs(".\n", of); + } + dirlen = (size_t)nam.nam$b_dev + nam.nam$b_dir; + memcpy(dir, rsa, dirlen); + dir[dirlen] = '\0'; + if( options & dir_heading) + printmsg( DIRECT_DIRHEAD, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dir ); + stats->filecount += dirfiles; + stats->totblocks += dirblocks; + stats->totalloc += diralloc; + stats->dircount++; + dirfiles = 0; + dirblocks = 0; + diralloc = 0; + printcol = 0; + } + rsa[nam.nam$b_rsl] = '\0'; + namelen = (size_t)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) { + size_t newcol = (printcol + 20) / 20 * 20; + if (newcol + namelen >= 80) { + fputs( "\n", of ); + printcol = 0; + } else { + fprintf( of, "%*s", (int)(newcol - printcol), " " ); + printcol = newcol; + } + } + fputs( rsa + dirlen, of ); + printcol += namelen; + } + } else { + if (options & dir_names) { + if ( (options & dir_heading) == 0 ) + fprintf( of, "%s", dir ); + if( options & dir_pack ) { + if( namelen > 18 ) { + fprintf( of, "%s", rsa + dirlen ); + if( options & dir_extra ) + fprintf( of, "\n " ); + } else { + fprintf( of, "%-19s", rsa + dirlen ); + } + } else + fprintf( of, "%s\n", rsa + dirlen ); + } + if( $FAILS(status = sys_open(&fab)) ) { + printmsg( status, MSG_TOFILE, of ); + status = SS$_NORMAL; + } else { + status = 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) + fprintf( of, "%sFile ID:", (options & dir_pack)? " ": "" ); + (void) snprintf(fileid, sizeof(fileid), "(%u,%u,%u)", + (nam.nam$w_fid.fid$b_nmx << 16) | nam.nam$w_fid.fid$w_num, + nam.nam$w_fid.fid$w_seq, nam.nam$w_fid.fid$b_rvn ); + fprintf( of, " %-22s", fileid ); + } + diralloc += fab.fab$l_alq; + if (options & dir_used) { + uint32_t filesize; + + filesize = fhc.xab$l_ebk; /* VBN of eof */ + if( filesize == 0 ) /* none, use allocated size */ + filesize = fab.fab$l_alq; + else { + 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) + printmsg( DIRECT_FULLSIZE, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, filesize ); + else + fprintf( of, "%9u", filesize ); + if( options & (dir_allocated|dir_full)) + fprintf( of, "/%-9u", fab.fab$l_alq ); + if( options & (dir_allocated | dir_full) ) { + if( options & dir_pack ) + fputs( " ", of ); + else + fputc( '\n', of ); + } + } + } else { + if ( (options & (dir_allocated|dir_names)) == (dir_allocated|dir_names)) { + fprintf( of, "%9u", fab.fab$l_alq ); + } + } +#define pprot(val, pos, del) do { \ + unsigned int v = ~(((val) >> (pos)) & xab$m_prot); \ + if( v & xab$m_noread ) fprintf( of, "R" ); \ + if( v & xab$m_nowrite ) fprintf( of, "W" ); \ + if( v & xab$m_noexe ) fprintf( of, "E" ); \ + if( v & xab$m_nodel ) fprintf( of, "D" ); \ + fprintf( of, del ); \ + } while( 0 ) + + if (options & dir_full) { + int pos = 0; + + printmsg( DIRECT_OWNER, MSG_TEXT|MSG_TOFILE, of, + ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF); + printmsg( DIRECT_CREATED, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); + prvmstime( of, dat.xab$q_cdt, "\n" ); + printmsg( DIRECT_REVISED, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); + prvmstime( of, dat.xab$q_rdt, " (" ); fprintf( of, "%u)\n", dat.xab$w_rvn ); + printmsg( DIRECT_EXPIRES, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); + prvmstime( of, dat.xab$q_edt, "\n" ); + printmsg( DIRECT_BACKUP, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of ); + prvmstime( of, dat.xab$q_bdt, "\n" ); + +#define MSG(x) getmsg( DIRECT_ ## x, MSG_TEXT|MSG_NOCRLF ) + pwrap( of, &pos, MSG( FILEORG ) ); + switch( fab.fab$b_org ) { + case FAB$C_SEQ: + pwrap( of, &pos, MSG( SEQORG) ); break; + case FAB$C_REL: + pwrap( of, &pos, MSG( RELORG ) /*, Maximum record number %u", fab.fab$l_mrn*/ ); break; + case FAB$C_IDX: + pwrap( of, &pos, MSG( IDXORG ) ); break; /*, Prolog: 3, Using 4 keys\nIn 3 areas */ + default: + pwrap( of, &pos, MSG( UNKORG ), fab.fab$b_org ); break; + } + + pwrap( of, &pos, MSG( FILEATT ) ); + pwrap( of, &pos, MSG( ALLOC ), fab.fab$l_alq ); + pwrap( of, &pos, MSG( EXTEND ), fab.fab$w_deq ); + /* Missing: , Maximum bucket size: n*/ + pwrap( of, &pos, MSG( GBC ), fab.fab$w_gbc ); + + if( fhc.xab$w_verlimit == 0 || fhc.xab$w_verlimit == 32767 ) { + if( directory ) + pwrap( of, &pos, MSG(NODIRLIMIT) ); + else + pwrap( of, &pos, MSG(NOVERLIMIT ) ); + } else { + if( directory ) + pwrap( of, &pos, MSG(DIRLIMIT), fhc.xab$w_verlimit ); + else + pwrap( of, &pos, MSG(VERLIMIT), fhc.xab$w_verlimit ); + } +#define pattr(bit, text) \ + if( bit ) \ + pwrap( of, &pos, MSG(text) ) + pattr( contig, CONTIG ); + pattr( contigb, CONTIGB ); + pattr( nobackup, NOBACKUP ); + pattr( directory, DIRECTORY ); + pattr( writeback, WRITEBACK ); + pattr( readcheck, READCHECK ); + pattr( writecheck, WRITECHECK ); + pattr( locked, LOCKED ); + pattr( spool, SPOOL ); + pattr( markdel, MARKDEL ); + pattr( badblock, BADBLOCK ); + pwrap( of, &pos, "\n" ); +#undef pattr + pwrap( of, &pos, MSG(EOFPOS), fhc.xab$l_ebk, fhc.xab$w_ffb ); + + pwrap( of, &pos, MSG(RECFMT) ); + switch( fab.fab$b_rfm ) { + default: + case FAB$C_UDF: + pwrap( of, &pos, MSG(RECUDF) ); break; + case FAB$C_FIX: + pwrap( of, &pos, MSG(RECFIX), fab.fab$w_mrs ); break; + case FAB$C_VAR: + pwrap( of, &pos, MSG(RECVAR), fhc.xab$w_lrl ); break; + case FAB$C_VFC: + pwrap( of, &pos, MSG(RECVFC), + (fab.fab$b_fsz? fab.fab$b_fsz: 2), + fhc.xab$w_lrl ); break; + case FAB$C_STM: + pwrap( of, &pos, MSG(RECSTM) ); break; + case FAB$C_STMLF: + pwrap( of, &pos, MSG(RECSTMLF) ); break; + case FAB$C_STMCR: + pwrap( of, &pos, MSG(RECSTMCR) ); break; + } + pwrap( of, &pos, "\n" ); + + pwrap( of, &pos, MSG(RECATT) ); + if( fab.fab$b_rat == 0 ) + pwrap( of, &pos, MSG(RECATT_NONE) ); + else { + const char *more = ""; + if( fab.fab$b_rat & FAB$M_FTN ) { + pwrap( of, &pos, MSG(RECATT_FTN), more ); + more = ", "; + } + if( fab.fab$b_rat & FAB$M_CR ) { + pwrap( of, &pos, MSG(RECATT_CR), more ); + more = ", "; + } + if( fab.fab$b_rat & FAB$M_PRN ) { + pwrap( of, &pos, MSG(RECATT_PRN), more ); + more = ", "; + } + if( fab.fab$b_rat & FAB$M_BLK ) { + pwrap( of, &pos, MSG(RECATT_BLK), more ); + } + } + fprintf( of, "\n" ); + /* + * RMS attributes: None + * Journaling enabled: None + */ + fprintf( of, MSG(PROT_SYS) ); + pprot(pro.xab$w_pro, xab$v_system, MSG(PROT_OWNER) ); + pprot(pro.xab$w_pro, xab$v_owner, MSG(PROT_GROUP) ); + pprot(pro.xab$w_pro, xab$v_group, MSG(PROT_WORLD) ); + pprot(pro.xab$w_pro, xab$v_world, "\n"); + + if( options & dir_mappings ) { + struct dsc$descriptor devnam; + unsigned clustersize = 0; + struct HEAD *buf = NULL; + struct fiddef lastfid, fid; + unsigned vbn = 1; + + memset( &devnam, 0, sizeof( devnam ) ); + devnam.dsc$b_dtype = DSC$K_DTYPE_T; + devnam.dsc$b_class = DSC$K_CLASS_S; + devnam.dsc$w_length = nam.nam$b_dev; + devnam.dsc$a_pointer = nam.nam$l_dev; + + fid = nam.nam$w_fid; + do { + if( $SUCCESSFUL(status = get_fileheader( of, + &devnam, + &fid, + &clustersize, + &buf )) ) { + print_fileheader( of, buf, &vbn, clustersize ); + } + lastfid = fid; + fid = buf->fh2$w_ext_fid; + free( buf ); + buf = NULL; + } while( $SUCCESSFUL(status) && + (fid.fid$w_num | fid.fid$w_seq | fid.fid$b_rvn | + fid.fid$b_nmx) != 0 && + memcmp( &fid, &lastfid, sizeof( fid ) ) != 0 ); + } + } else { /* !full */ + if (options & dir_date) { + if( options & dir_created ) + status = prvmstime( of, dat.xab$q_cdt, NULL ); + if( options & dir_modified ) + status = prvmstime( of, dat.xab$q_rdt, NULL ); + if( options & dir_expired ) + status = prvmstime( of, dat.xab$q_edt, NULL ); + if( options & dir_backup ) + status = prvmstime( of, dat.xab$q_bdt, NULL ); + } + if (options & dir_owner) { + printmsg( DIRECT_UIC, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, + ((pro.xab$l_uic>>16)&0xFFFF), pro.xab$l_uic&0xFFFF ); + } + if (options & dir_prot) { + fprintf( of, " (" ); + 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) + fprintf( of, "\n" ); + } + } + dirfiles++; + } + if( status == RMS$_NMF ) + status = SS$_NORMAL; + if( printcol > 0 ) fprintf( of, "\n" ); + if (options & dir_trailing) { + printmsg( DIRECT_FILETOTAL, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, dirfiles ); + dirtotal( of, options, dirblocks, diralloc ); + fputs( ".\n", of ); + } + stats->filecount += dirfiles; + stats->totblocks += dirblocks; + stats->totalloc += diralloc; + } + + fab.fab$b_fns = + nam.nam$b_ess = + fab.fab$b_dns = 0; + nam.nam$b_nop = NAM$M_SYNCHK; + (void) sys_parse( &fab ); + + return status; +} + +/*********************************************************** dirtotal() */ + +static void dirtotal( FILE *of, options_t options, uint32_t size, uint32_t alloc ) { + if ( !(options & dir_size) ) + return; + fputs( ", ", of ); + + switch( options & (dir_used | dir_allocated) ) { + case dir_used: + printmsg( DIRECT_USEDTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, size ); + break; + case dir_allocated: + printmsg( DIRECT_ALLOCTOT, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, alloc ); + break; + case dir_used | dir_allocated: + printmsg( DIRECT_BOTHSIZE, MSG_TEXT|MSG_NOCRLF|MSG_TOFILE, of, size, alloc ); + break; + default: + return; + } +} + +/*********************************************************** get_fileheader() */ +static unsigned get_fileheader( FILE *of, struct dsc$descriptor *devname, + struct fiddef *fid, unsigned *clustersize, + struct HEAD **buf ) { + unsigned status; + char *idxbuf; + struct HOME *hom; + long vbn; + struct FAB idxfab = cc$rms_fab; + struct RAB idxrab = cc$rms_rab; + struct NAM idxnam = cc$rms_nam; + char rsbuf[NAM$C_MAXRSS + 1] = { "" }; + + if( devname->dsc$w_length > NAM$S_DVI ) + return RMS$_FNM; + + idxbuf = (char *)malloc( 512 ); + *buf = (struct HEAD *) idxbuf; + if( idxbuf == NULL ) + return SS$_INSFMEM; + + idxfab.fab$l_fna = NULL; + idxfab.fab$b_fns = 0; + idxfab.fab$l_dna = NULL; + idxfab.fab$b_dns = 0; + idxfab.fab$b_fac = FAB$M_GET; + + idxfab.fab$l_fop = FAB$M_NAM; + idxnam.nam$t_dvi[0] = (char)devname->dsc$w_length; + memcpy( idxnam.nam$t_dvi + 1, devname->dsc$a_pointer, devname->dsc$w_length ); + + idxnam.nam$w_did.fid$w_num = 4; + idxnam.nam$w_did.fid$w_seq = 4; + idxnam.nam$w_did.fid$b_nmx = 0; + idxnam.nam$w_did.fid$b_rvn = fid->fid$b_rvn; + + idxnam.nam$w_fid.fid$w_num = 1; + idxnam.nam$w_fid.fid$w_seq = 1; + idxnam.nam$w_fid.fid$b_nmx = 0; + idxnam.nam$w_fid.fid$b_rvn = fid->fid$b_rvn; + idxnam.nam$l_rsa = rsbuf; + idxnam.nam$b_rss = NAM$C_MAXRSS; + + do { + idxfab.fab$l_nam = &idxnam; + if( $FAILS(status = sys_open(&idxfab)) ) + break; + + idxrab.rab$l_fab = &idxfab; + if( $FAILS(status = sys_connect( &idxrab )) ) { + sys_close( &idxfab ); + break; + } + + idxrab.rab$l_ubf = idxbuf; + idxrab.rab$w_usz = 512; + idxrab.rab$w_rfa[2] = 0; + idxrab.rab$w_rfa[1] = (uint16_t) (2 >> 16); /* HOMVBN */ + idxrab.rab$w_rfa[0] = (uint16_t) (2); + + if( $FAILS(status = sys_get( &idxrab )) ) + break; + + hom = (struct HOME *)idxbuf; + vbn = ( F11WORD( hom->hm2$w_ibmapvbn ) + + F11WORD( hom->hm2$w_ibmapsize ) + + ((fid->fid$b_nmx << 16) | + fid->fid$w_num) -1 ); + *clustersize = F11WORD( hom->hm2$w_cluster ); + + idxrab.rab$w_rfa[2] = 0; + idxrab.rab$w_rfa[1] = (uint16_t) (vbn >> 16); + idxrab.rab$w_rfa[0] = (uint16_t) (vbn); + + status = sys_get( &idxrab ); + } while( 0 ); + + sys_disconnect( &idxrab ); + sys_close( &idxfab ); + if( $FAILED(status) ) { + printmsg( ODS2_FILHDRACC, 0 ); + status = printmsg( status, MSG_CONTINUE|MSG_NOARGS|MSG_TOFILE, of, ODS2_FILHDRACC ); + } + return status; +} + +/*********************************************************** print_fileheader() */ + +static void print_fileheader( FILE *of, struct HEAD *fhd, uint32_t *vbn, + uint32_t clustersize ) { + f11word *mp, *ep; + f11long hiblk; + struct IDENT *id; + size_t idsize; + char fname[ sizeof( id->fi2$t_filename ) + sizeof( id->fi2$t_filenamext ) + 1 ]; + + id = (struct IDENT *)(((f11word *)fhd) + fhd->fh2$b_idoffset); + idsize = (size_t)(fhd->fh2$b_mpoffset - fhd->fh2$b_idoffset) * 2; + + if( idsize >= sizeof( id->fi2$t_filename ) ) { + char *p; + + memset( fname, ' ', sizeof( fname ) ); + memcpy( fname, id->fi2$t_filename, sizeof( id->fi2$t_filename )); + idsize -= offsetof( struct IDENT, fi2$t_filenamext ); + if( idsize ) { + if( idsize > sizeof( id->fi2$t_filenamext ) ) + idsize = sizeof( id->fi2$t_filenamext ); + memcpy( fname + sizeof( id->fi2$t_filename ), id->fi2$t_filenamext, idsize ); + } + for( p = fname + sizeof(fname); p > fname && p[-1] == ' '; --p ) + p[-1] = '\0'; + fname[sizeof(fname) -1] = '\0'; + printmsg( DIRECT_IDNAME, MSG_TEXT|MSG_TOFILE, of, fname ); + } + + hiblk = F11SWAP( fhd->fh2$w_recattr.fat$l_hiblk ); + + if( fhd->fh2$w_seg_num != 0 ) + printmsg( DIRECT_EXTNHDR, MSG_TEXT|MSG_TOFILE, of, + F11WORD( fhd->fh2$w_seg_num ), + (((uint32_t)fhd->fh2$w_ext_fid.fid$b_nmx) << 16 | + F11WORD(fhd->fh2$w_ext_fid.fid$w_num)), + F11WORD(fhd->fh2$w_ext_fid.fid$w_seq), + fhd->fh2$w_ext_fid.fid$b_rvn ); + + printmsg( DIRECT_HDROFFSETS, MSG_TEXT|MSG_TOFILE, of, fhd->fh2$b_idoffset, + fhd->fh2$b_mpoffset, fhd->fh2$b_acoffset, fhd->fh2$b_rsoffset ); + printmsg( DIRECT_MAPSIZE, MSG_TEXT|MSG_TOFILE, of, + fhd->fh2$b_acoffset - fhd->fh2$b_mpoffset, + fhd->fh2$b_map_inuse, clustersize, hiblk ); + + if( fhd->fh2$b_idoffset >= 40 && fhd->fh2$l_highwater ) + printmsg( DIRECT_MAPHIGHWATER, MSG_TEXT|MSG_TOFILE, of, + F11LONG( fhd->fh2$l_highwater ) - 1 ); + + printmsg( DIRECT_STRUCLEVEL, MSG_TEXT|MSG_TOFILE, of, + fhd->fh2$w_struclev >> 8, fhd->fh2$w_struclev & 0xFFu ); + + mp = ((f11word *)fhd) + fhd->fh2$b_mpoffset; + ep = mp + fhd->fh2$b_map_inuse; + + if( mp >= ep ) + return; + + printmsg( DIRECT_MAPHEADER, MSG_TEXT|MSG_TOFILE, of ); + + while( mp < ep ) { + f11word e0; + uint32_t 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: + printmsg( DIRECT_MAPFMT0, MSG_TEXT|MSG_TOFILE, of, e0 ); + continue; + } + ++count; + printmsg( DIRECT_MAPENTRY, MSG_TEXT|MSG_TOFILE, of, *vbn, + (e0 >> 14), count, + pbn, pbn + count -1 ); + *vbn += count; + } + printmsg( DIRECT_MAPFREE, MSG_TEXT|MSG_TOFILE, of, *vbn ); + return; +} + diff --git a/extracters/ods2/direct.c b/extracters/ods2/direct.c index e553c1a..82b9380 100644 --- a/extracters/ods2/direct.c +++ b/extracters/ods2/direct.c @@ -1,1199 +1,1199 @@ -/* Direct.c */ - -/* - * 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -/* 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 -#include -#include -#include -#include -#include - -#include "access.h" -#include "descrip.h" -#include "direct.h" -#include "fibdef.h" -#include "ods2.h" -#include "ssdef.h" -#include "stsdef.h" -#include "sysmsg.h" - -#define BLOCKSIZE 512 -#define MAXREC (BLOCKSIZE - 2) - -/* Some statistical counters... */ - -static int direct_lookups = 0; -static int direct_searches = 0; -static int direct_deletes = 0; -static int direct_inserts = 0; -static int direct_splits = 0; -static int direct_checks = 0; -static int direct_matches = 0; - -/************************************************************** direct_show() */ - -/* direct_show - to print directory statistics */ - -void direct_show( void ) { - printf( "DIRECT_SHOW Lookups: %d ", direct_lookups ); - printf( "Searches: %d ", direct_searches ); - printf( "Deletes: %d ", direct_deletes ); - printf( "Inserts: %d ", direct_inserts ); - printf( "Splits: %d\n", direct_splits ); -} - -/*************************************************************** name_check() */ - -/* name_check() - take a name specification and return name length without - the version number, an integer version number, and a wildcard flag */ - -static vmscond_t name_check( char *str, int len, int *retlen, - int *retver, int *wildflag ) { - int wildcard = FALSE; - char *name_start = str; - register int dots = 0; - register char *name = name_start; - register char *name_end = name + len; - direct_checks++; - - /* Go through the specification checking for illegal characters */ - - while (name < name_end) { - register char ch = *name++; - if (ch == '.') { - if ((name - name_start) > 40) - return SS$_BADFILENAME; - name_start = name; - if (dots++ > 1) - break; - } else { - if (ch == ';') { - break; - } else { - if (ch == '*' || ch == '%') { - wildcard = TRUE; - } else { - if (ch == '[' || ch == ']' || ch == ':' || - !isprint(ch)) - return SS$_BADFILENAME; - } - } - } - } - if ((name - name_start) > 40) - return SS$_BADFILENAME; - - /* Return the name length and start checking the version */ - - *retlen = (int)(name - str - 1); - dots = 0; - if (name < name_end) { - register char ch = *name; - if (ch == '*') { - if (++name < name_end) - return SS$_BADFILENAME; - dots = 32768; /* Wildcard representation of version! */ - wildcard = TRUE; - } else { - register int sign = 1; - if (ch == '-') { - name++; - sign = -1; - } - while (name < name_end) { - ch = *name++; - if (!isdigit(ch)) - return SS$_BADFILENAME; - dots = dots * 10 + (ch - '0'); - } - dots *= sign; - } - } - *retver = dots; - *wildflag = wildcard; - return SS$_NORMAL; -} - -/*************************************************************** name_match() */ - -#define MAT_LT 0 -#define MAT_EQ 1 -#define MAT_GT 2 -#define MAT_NE 3 - -/* name_match() - compare a name specification with a directory entry - and determine if there is a match, too big, too small... */ - -int name_match(char *spec, int spec_len, char *dirent, int dirent_len) { - int percent = MAT_GT; - register char *name = spec, *entry = dirent; - register char *name_end = name + spec_len, *entry_end = entry + dirent_len; - direct_matches++; - - /* See how much name matches without wildcards... */ - - while (name < name_end && entry < entry_end) { - register char sch = *name; - if (sch != '*') { - register char ech = *entry; - if (sch != ech) { - if (toupper(sch) != toupper(ech)) { - if (sch == '%') { - percent = MAT_NE; - } else { - break; - } - } - } - } else { - break; - } - name++; - entry++; - } - - /* Mismatch - return result unless wildcard... */ - - if (name >= name_end) { - if (entry >= entry_end) { - return MAT_EQ; - } else { - return percent; - } - } else { - /* See if we can find a match with wildcards */ - - if (*name != '*') { - if (percent == MAT_NE) - return MAT_NE; - if (entry < entry_end) - if (toupper(*entry) > toupper(*name)) - return MAT_GT; - return MAT_LT; - } - /* Strip out wildcard(s) - if end then we match! */ - - do { - name++; - } while (name < name_end && *name == '*'); - if (name >= name_end) - return MAT_EQ; - - /* Proceed to examine the specification past the wildcard... */ - - while (name < name_end) { - register int offset = 1; - register char fch = toupper(*name++); - - /* See if can can find a match for the first character... */ - - if (fch != '%') { - while (entry < entry_end) { - if (toupper(*entry) != fch) { - entry++; - } else { - break; - } - } - } - /* Give up if we can't find that one lousy character... */ - - if (entry >= entry_end) - return MAT_NE; - entry++; - - /* See how much of the rest we can match... */ - - while (name < name_end && entry < entry_end) { - register char sch = *name, ech; - if (sch == '*') - break; - if (sch != (ech = *entry)) - if (toupper(sch) != toupper(ech)) - if (sch != '%') - break; - name++; - entry++; - offset++; - } - - /* If matching died because of a wildcard we are OK... */ - - if (name < name_end && *name == '*') { - do { - name++; - } while (name < name_end && *name == '*'); - if (name >= name_end) - return MAT_EQ; - - /* Otherwise we finished OK or we need to try again... */ - - } else { - if (name >= name_end && entry >= entry_end) - return MAT_EQ; - name -= offset; - entry -= (size_t)offset - 1; - } - } - } - - /* No more specification - match depends on remainder of entry... */ - - if (entry < entry_end) - return MAT_NE; - return MAT_EQ; -} - -/*************************************************************** insert_ent() */ - -/* insert_ent() - procedure to add a directory entry at record dr entry de */ - -vmscond_t insert_ent( struct FCB * fcb, uint32_t eofblk, uint32_t curblk, - struct VIOC * vioc, char *buffer, - struct dir$r_rec * dr, struct dir$r_ent * de, - char *filename, uint32_t filelen, - uint32_t version, struct fiddef * fid, - uint16_t *reslen, struct dsc$descriptor *resdsc ) { - register vmscond_t sts = SS$_NORMAL; - register size_t inuse = 0; - register uint16_t addlen; - uint16_t verlimit; - size_t avail = 0, len; - char *p, vbuf[sizeof(";65535")]; - - verlimit = version >> 16; - version &= 0xffff; - - /* Return result */ - - if( resdsc != NULL && resdsc->dsc$w_length != 0 ) { - avail = resdsc->dsc$w_length; - p = resdsc->dsc$a_pointer; - len = filelen; - if( len > avail ) - len = avail; - memcpy( p, filename, len ); - avail -= len; - p += len; - len = snprintf( vbuf, sizeof( vbuf ), ";%u", version ); - if( len > avail ) - len = avail; - memcpy( p, vbuf, len ); - p += len; - *reslen = (unsigned short)(p - resdsc->dsc$a_pointer); - } - - /* Compute space required... */ - - addlen = sizeof(struct dir$r_ent); - direct_inserts++; - if (de == NULL) - addlen += (((size_t)filelen + 1) & ~1) + offsetof(struct dir$r_rec, dir$t_name); - - /* Compute block space in use ... */ - - { - int invalid_dir = TRUE; - while (TRUE) { - register size_t reclen; - register struct dir$r_rec *nr; - - nr = (struct dir$r_rec *) (buffer + inuse); - - if( dr == nr ) - invalid_dir = FALSE; - reclen = F11WORD(nr->dir$w_size); - if (reclen == 0xffff) /* End of data marker */ - break; - reclen += 2; - inuse += reclen; - reclen -= (((size_t)nr->dir$b_namecount + 1) & ~1) + offsetof(struct dir$r_rec, dir$t_name); - if (inuse > MAXREC || (inuse & 1) || reclen <= 0 || - reclen % 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 (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); - } - } - } - - /* If not enough space free extend the directory... */ - - if (addlen > MAXREC - inuse) { - register struct dir$r_rec *nr; - unsigned keep_new = 0; - char *newbuf; - struct VIOC *newvioc; - uint32_t newblk = eofblk + 1; - - direct_splits++; -#if DEBUG - printf( "Splitting directory 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' ); -#endif - if( newblk > fcb->hiblock ) { -#if DEBUG - printf("Extending directory during split\n"); -#endif - if( $FAILS(sts = update_extend( fcb, newblk - fcb->hiblock, 0 )) ) { -#if DEBUG - printf( "Failed to extend a directory during a split\n" ); -#endif - return $SETLEVEL(SS$_DIRALLOC, SEVERE); - } - } - - if( fcb->highwater != 0 && newblk > fcb->highwater -1 ) { - fcb->highwater = newblk + 1; - fcb->head->fh2$l_highwater = F11LONG( newblk + 1 ); - } - - if( $SUCCESSFUL(sts = accesschunk( fcb, newblk, &newvioc, &newbuf, NULL, 1 )) ) { - while( newblk > curblk + 1 ) { - char *frombuf; - struct VIOC *fromvioc; - - if( $FAILED(sts = accesschunk( fcb, newblk - 1, &fromvioc, &frombuf, NULL, 1 )) ) - break; - memcpy( newbuf, frombuf, BLOCKSIZE ); - sts = deaccesschunk( newvioc, newblk, 1, TRUE); - newvioc = fromvioc; - newbuf = frombuf; - newblk--; - if( $FAILED(sts) ) - break; - } - } else { - newvioc = NULL; - } - if( $FAILED(sts) ) { - if( newvioc != NULL ) - deaccesschunk( newvioc, 0, 0, FALSE); - deaccesschunk( vioc, 0, 0, FALSE ); - return sts; - } - memset( newbuf, 0, BLOCKSIZE ); - eofblk++; - fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP( eofblk + 1 ); - - /* First find where the next record is... */ - - nr = dr; - 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 || - F11WORD(nr->dir$w_size) <= MAXREC) { - register struct dir$r_rec *sp; - - 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$w_size = F11WORD(0xffff); - if (sp == dr && - (de != NULL || (char *) sp >= buffer + MAXREC - addlen)) { - if (de != NULL) - de = (struct dir$r_ent *) - (newbuf + ((char *) de - (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 uint32_t reclen; - register struct dir$r_rec *nbr; - - reclen = ( (((size_t)dr->dir$b_namecount + 1) & ~1) + - offsetof(struct dir$r_rec, dir$t_name) ); - nbr = (struct dir$r_rec *) newbuf; -#if DEBUG - 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); -#endif - memcpy(newbuf, buffer, reclen); - memcpy(newbuf + reclen, de, ((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$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$r_rec *) newbuf; - de = (struct dir$r_ent *) (newbuf + reclen); - keep_new = 1; - } - } - - /* Need to decide which buffer we are going to keep (to write to) */ - - if (keep_new) { - sts = deaccesschunk( vioc, curblk, 1, TRUE); - curblk = newblk; - vioc = newvioc; - buffer = newbuf; - } else { - sts = deaccesschunk( newvioc, newblk, 1, TRUE ); - } - if( $FAILED(sts) ) - printf("I/O error splitting directory record: %s\n", getmsg(sts, 0)); - } - /* After that we can just add the record or entry as appropriate... */ - - if (de == NULL) { - memmove((char *) dr + addlen, dr, - BLOCKSIZE - (((char *) dr + addlen) - buffer)); - dr->dir$w_size = F11WORD(addlen - 2); - dr->dir$w_verlimit = verlimit; - 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$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$w_version = F11WORD(version); - fid_copy( &de->dir$w_fid, fid, 0); - return deaccesschunk( vioc, curblk, 1, TRUE ); -} - -/*************************************************************** delete_ent() */ - -/* delete_ent() - delete a directory entry */ - -vmscond_t delete_ent( struct FCB * fcb, struct VIOC * vioc, uint32_t curblk, - struct dir$r_rec * dr, struct dir$r_ent * de, - char *buffer, uint32_t eofblk ) { - vmscond_t sts = SS$_NORMAL; - uint32_t ent; - - direct_deletes++; - ent = ((size_t)F11WORD(dr->dir$w_size) + 2 - offsetof(struct dir$r_rec, dir$t_name) - - (((size_t)dr->dir$b_namecount + 1) & ~1)) / sizeof(struct dir$r_ent); - if (ent > 1) { - char *ne; - - ne = (char *) de + sizeof(struct dir$r_ent); - memmove( de, ne, BLOCKSIZE - (ne - buffer) ); - dr->dir$w_size = F11WORD(F11WORD(dr->dir$w_size) - sizeof(struct dir$r_ent)); - } else { - char *nr; - - nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; - if( eofblk == 1 || (char *) dr > buffer || - (nr <= buffer + MAXREC && (unsigned short) *nr < BLOCKSIZE) ) { - memmove(dr, nr, BLOCKSIZE - (nr - buffer)); - } else { - while (curblk < eofblk) { - char *nxtbuffer; - struct VIOC *nxtvioc; - - sts = accesschunk(fcb, curblk + 1, &nxtvioc, &nxtbuffer, NULL, 1); - if( $FAILED(sts) ) - break; - memcpy(buffer, nxtbuffer, BLOCKSIZE); - if( $FAILS(sts = deaccesschunk(vioc, curblk++, 1, TRUE)) ) - break; - buffer = nxtbuffer; - vioc = nxtvioc; - } - if( $SUCCESSFUL(sts) ) { - fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP(eofblk); - eofblk--; - } - } - } - { - vmscond_t retsts; - - retsts = deaccesschunk(vioc, curblk, 1, TRUE); - if( $SUCCESSFUL(sts) ) - sts = retsts; - return sts; - } -} - -/*************************************************************** return_ent() */ - -/* return_ent() - return information about a directory entry */ - -vmscond_t return_ent(struct FCB * fcb, struct VIOC * vioc, uint32_t curblk, - struct dir$r_rec * dr, struct dir$r_ent * de, struct fibdef * fib, - uint16_t *reslen, struct dsc_descriptor * resdsc, - int wildcard) { - if( resdsc != NULL ) { - register uint32_t scale = 10; - register uint16_t version; - register size_t length; - register char *ptr; - register size_t outlen; - - version = F11WORD(de->dir$w_version); - length = dr->dir$b_namecount; - ptr = resdsc->dsc_a_pointer; - outlen = resdsc->dsc_w_length; - - if (length > outlen) - length = outlen; - memcpy(ptr, dr->dir$t_name, length); - while( version >= scale ) - scale *= 10; - ptr += length; - if( length < outlen ) { - *ptr++ = ';'; - length++; - do { - if( length >= outlen ) - break; - scale /= 10; - *ptr++ = version / scale + '0'; - version %= scale; - length++; - } while( scale > 1 ); - } - *reslen = (uint16_t)length; - } - 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 { - fib->fib$l_wcc = 0; - } - return deaccesschunk(vioc, 0, 0, TRUE); -} - -/*************************************************************** search_ent() */ - -/* search_ent() - search for a directory entry */ - -vmscond_t search_ent( struct FCB *fcb, - struct dsc_descriptor *fibdsc, - struct dsc_descriptor *filedsc, uint16_t *reslen, - struct dsc_descriptor *resdsc, uint32_t eofblk, - unsigned action ) { - register vmscond_t sts; - uint32_t curblk; - struct VIOC *vioc = NULL; - char *searchspec, *buffer; - int searchlen, version, wildcard, wcc_flag; - struct fibdef *fib; - - fib = (struct fibdef *) fibdsc->dsc_a_pointer; - direct_lookups++; - - /* 1) Generate start block (wcc gives start point) - * 2) Search for start - * 3) Scan until found or too big or end. - */ - - curblk = fib->fib$l_wcc; - if (curblk != 0) { - searchspec = resdsc->dsc_a_pointer; - sts = name_check( searchspec, *reslen, &searchlen, &version, &wildcard ); - if( MODIFIES(action) || wildcard ) - sts = RMS$_WLD; - wcc_flag = TRUE; - } else { - searchspec = filedsc->dsc_a_pointer; - sts = name_check( searchspec, filedsc->dsc_w_length, &searchlen, &version, - &wildcard); - if( (MODIFIES(action) && wildcard) || (action == DIRECT_CREATE && version < 0) ) { - sts = RMS$_WLD; - } - wcc_flag = FALSE; - } - if( $FAILED(sts) ) - return sts; - - /* Identify starting block...*/ - - if (*searchspec == '*' || *searchspec == '%') { - curblk = 1; - } else { - register uint32_t loblk = 1, hiblk = eofblk; - if( curblk < 1 || curblk > eofblk ) - curblk = (eofblk + 1) / 2; - - while (loblk < hiblk) { - register int cmp; - register uint32_t newblk; - register struct dir$r_rec *dr; - direct_searches++; - - if( $FAILS(sts = accesschunk( fcb, curblk, &vioc, &buffer, NULL, MODIFIES(action) )) ) - return sts; - dr = (struct dir$r_rec *) buffer; - if( F11WORD(dr->dir$w_size) > MAXREC ) { - cmp = MAT_GT; - } else { - 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$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 (F11WORD(de->dir$w_version) > version) { - cmp = MAT_LT; /* further ahead... */ - } - } - } - } - } - switch (cmp) { - case MAT_LT: - if (curblk == fib->fib$l_wcc) { - newblk = hiblk = loblk = curblk; - } else { - loblk = curblk; - newblk = (loblk + hiblk + 1) / 2; - } - break; - case MAT_GT: - case MAT_NE: - newblk = (loblk + curblk) / 2; - hiblk = curblk - 1; - break; - default: - newblk = hiblk = loblk = curblk; - } - if (newblk != curblk) { - if( $FAILS(sts = deaccesschunk( vioc, 0, 0, TRUE )) ) - return sts; - vioc = NULL; - curblk = newblk; - } - } - } - - /* Now to read sequentially to find entry... */ - - { - char last_name[80]; - unsigned last_len = 0; - register int relver = 0; - while( $SUCCESSFUL(sts) && curblk <= eofblk ) { - 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 ) { - if( $FAILS(sts = accesschunk( fcb, curblk, &vioc, - &buffer, NULL, MODIFIES(action) )) ) - return sts; - } - 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$t_name, last_len) != - MAT_EQ ) { - relver = 0; - } - } - - /* Now loop through the records seeing which match our spec... */ - - while (TRUE) { /* dr records within block */ - register char *nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; - if (nr >= buffer + BLOCKSIZE) - break; - 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; - if( $FAILS(sts = name_check(searchspec, filedsc->dsc_w_length, - &searchlen, &version, &wildcard)) ) - break; - } else { - if (cmp == MAT_EQ) { - 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 = RMS$_VER; - break; - } - } - /* Look at each directory entry to see - if it is what we want... */ - - if ((char *) dr != buffer) relver = 0; - cmp = MAT_LT; - while ((char *) de < nr) { - if ((version < 1) ? - (relver > version) : - (version < F11WORD(de->dir$w_version))) { - relver--; - de++; - } else { - if (version > 32767 || version == relver || - version == F11WORD(de->dir$w_version)) { - cmp = MAT_EQ; - } else { - cmp = MAT_GT; - } - if (!wcc_flag) { - break; - } else { - wcc_flag = FALSE; - searchspec = filedsc->dsc_a_pointer; - if( $FAILS(sts = name_check( searchspec, - filedsc->dsc_w_length, - &searchlen, &version, - &wildcard )) ) - break; - if (name_match(searchspec, searchlen, - dr->dir$t_name, - dr->dir$b_namecount) != - MAT_EQ) { - cmp = MAT_NE; - break; - } - if (version < 0) { - relver = -32768; - cmp = MAT_GT; - break; - } - if (cmp == MAT_EQ) { - relver--; - de++; - } - cmp = MAT_LT; - } - } - } - if( $FAILED(sts) ) - break; - - /* Decide what to do with the entry we have found... */ - - if (cmp == MAT_EQ) { - switch (action) { - 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 == DIRECT_CREATE ) { - return insert_ent( fcb, eofblk, curblk, vioc, buffer, - dr, de, searchspec, searchlen, - (fib->fib$w_verlimit << 16) | version, - (struct fiddef *)&fib->fib$w_fid_num, reslen, resdsc ); - - } - } - } - /* Finish unless we expect more... */ - - if (cmp == MAT_GT && !wildcard) - break; - - /* If this is the last record in the block store the name - so that if it continues into the next block we can get - the relative version right! Sigh! */ - - 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$t_name, last_len); - dr = (struct dir$r_rec *) nr; - break; - } - 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( $FAILED(sts) || action != DIRECT_CREATE || - (cmp != MAT_GT && curblk < eofblk)) { - register vmscond_t dests; - - if( $FAILS(dests = deaccesschunk( vioc, 0, 0, TRUE )) ) { - sts = dests; - break; - } - vioc = NULL; - curblk++; - } else { - if( version == 0 ) - version = 1; - return insert_ent( fcb, eofblk, curblk, vioc, buffer, dr, NULL, - searchspec, searchlen, (fib->fib$w_verlimit << 16) | version, - (struct fiddef *)&fib->fib$w_fid_num, - reslen, resdsc ); - } - } /* curblk blocks within file */ - } - - /* We achieved nothing! Report the failure... */ - - if( $SUCCESSFUL(sts) ) { - fib->fib$l_wcc = 0; - if (wcc_flag || wildcard) { - sts = SS$_NOMOREFILES; - } else { - sts = SS$_NOSUCHFILE; - } - } - return sts; -} - -/******************************************************************* direct() */ - -/* direct() - this routine handles all directory manipulations:- - action 0 - find directory entry - 1 - delete entry - 2 - create an entry - */ - -unsigned direct( struct VCB * vcb, struct dsc_descriptor * fibdsc, - struct dsc_descriptor * filedsc, uint16_t *reslen, - struct dsc_descriptor * resdsc, unsigned action ) { - struct FCB *fcb; - register vmscond_t sts; - uint32_t eofblk; - register struct fibdef *fib; - - fib = (struct fibdef *) fibdsc->dsc_a_pointer; - - if( $SUCCESSFUL(sts = accessfile( vcb, (struct fiddef *) &fib->fib$w_did_num, - &fcb, MODIFIES(action) )) ) { - 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; - if( eofblk == 0 ) { - struct VIOC *vioc; - char *buffer; - uint32_t blocks; - - if( action != DIRECT_CREATE ) { - int searchlen, version, wildcard; - - deaccessfile( fcb ); - if( fib->fib$l_wcc ) { - if( $FAILS(sts = name_check( resdsc->dsc$a_pointer, *reslen, - &searchlen, &version, &wildcard )) ) - return sts; - fib->fib$l_wcc = 0; - return SS$_NOMOREFILES; - } - if( $FAILS(sts = name_check( filedsc->dsc$a_pointer, - filedsc->dsc$w_length, - &searchlen, &version, &wildcard )) ) - return sts; - if( wildcard ) - return SS$_NOMOREFILES; - return SS$_NOSUCHFILE; - } - - eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); - if( eofblk == 0 ) { - if( $FAILS(sts = update_extend( fcb, 1, 0 )) ) { - deaccessfile( fcb ); - return sts; - } - eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); - if( fcb->highwater != 0 ) { - fcb->highwater = 2; - fcb->head->fh2$l_highwater = F11SWAP( 2 ); - } - } - fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP( 2 ); - fcb->head->fh2$w_recattr.fat$w_ffbyte = 0; - eofblk = 1; - if( $FAILS(sts = accesschunk( fcb, 1, &vioc, &buffer, &blocks, 1 )) ) { - deaccessfile( fcb ); - return sts; - } - memset( buffer, 0, 512 ); - ((f11word *)buffer)[0] = 0xffff; - deaccesschunk( vioc, 1, blocks, TRUE ); - } - sts = search_ent( fcb, fibdsc, filedsc, reslen, resdsc, eofblk, action ); - } else { - sts = SS$_BADIRECTORY; - } - { - register uint32_t dests; - - dests = deaccessfile( fcb ); - if( $SUCCESSFUL(sts) ) - sts = dests; - } - } - if( MODIFIES(action) ) { - cache_flush(); - while( vcb->dircache ) /* Too conservative...but safe */ - cache_delete( (struct CACHE *)vcb->dircache ); - } - - return sts; -} - -/******************************************************************* dircmp() */ - -/* Function to compare directory cache names */ - -static int dircmp( uint32_t keylen, void *key, void *node ) { - register struct DIRCACHE *dirnode; - register int cmp; - unsigned dlen; - const char *kp, *dp; - int version, wild, namelen; - vmscond_t sts; - - if( $FAILS(sts = name_check(key, keylen, &namelen, &version, &wild) ) ) { - abort(); - } - - dirnode = (struct DIRCACHE *) node; - - if( version != F11WORD(dirnode->entry.dir$w_version) ) - return version - F11WORD(dirnode->entry.dir$w_version); - - dlen = dirnode->record.dir$b_namecount; - - kp = (const char *)key; - dp = dirnode->record.dir$t_name; - - while( keylen > 0 && dlen > 0 ) { - cmp = toupper(*kp++) - toupper(*dp++); - if( cmp != 0 ) - return cmp; - --keylen; - --dlen; - } - return keylen - dlen; -} - -/***************************************************************** dir_create() */ -static void *dir_create( uint32_t keylen, void *key, vmscond_t *retsts ) { - struct DIRCACHE *dir; - vmscond_t sts; - int version, wild, namelen; - - if( $FAILS(sts = name_check(key, keylen, &namelen, &version, &wild) ) ) { - *retsts = RMS$_DIR; - return NULL; - } - if( wild || version == 0 ) { - *retsts = SS$_NOSUCHFILE; - return NULL; - } - - if( (dir = (struct DIRCACHE *)calloc( 1, - offsetof( struct DIRCACHE, record.dir$t_name ) + - namelen + (namelen & 1) )) == NULL ) { - *retsts = SS$_INSFMEM; - return NULL; - } - - dir->cache.objtype = OBJTYPE_DIR; - dir->record.dir$w_size = (f11word)F11WORD( offsetof( struct dir$r_rec, - dir$t_name ) + namelen - 2); - dir->record.dir$b_namecount = (f11byte) namelen; - memcpy( dir->record.dir$t_name, key, namelen ); - dir->entry.dir$w_version = (f11word)F11WORD((f11word)version); - - *retsts = SS$_CREATED; - return dir; -} - -/***************************************************************** direct_dirid() */ - -/* Find FID of directory/ies using a cache. - * Used by sys$parse. Can be generalized... - * Input descriptor is a directory name string, including subdirectories. - * Output is the fid of the lowest level directory. - * Higher directories are searched (and cached). - * Any wildcards/recursion stop the search (they'll be resolved when a - * search matches them.) - */ - -vmscond_t direct_dirid( struct VCB *vcb, struct dsc$descriptor *dirdsc, - struct fiddef *dirid, struct fiddef *fid ) { - register struct DIRCACHE *dir = NULL; - vmscond_t sts; - struct fibdef fib; - struct dsc$descriptor fibdsc; - struct dsc$descriptor namdsc; - struct fiddef srcdir; - char *dirnam; - int dirlen; - size_t len; - char *bp, *dp; - char nambuf[256]; - - memset( &fib, 0, sizeof( fib ) ); - memset( &fibdsc, 0, sizeof( fibdsc ) ); - memset( &namdsc, 0, sizeof( namdsc ) ); - memset( &srcdir, 0, sizeof( srcdir ) ); - - dirlen = dirdsc->dsc$w_length; - - if( (size_t)dirlen > sizeof( nambuf ) -1 ) - abort(); - dirnam = dirdsc->dsc$a_pointer; - - srcdir.fid$w_num = FID$C_MFD; - srcdir.fid$w_seq = FID$C_MFD; - srcdir.fid$b_rvn = 0; - srcdir.fid$b_nmx = 0; - - if( dirlen < 1 || (dirlen == 6 && !memcmp( dirnam, "000000", 6 )) ) { - if( dirid ) - *dirid = srcdir; - if( fid ) - *fid = srcdir; - return SS$_NORMAL; - } - - if( dirid != NULL ) - memset( dirid, 0, sizeof( *dirid ) ); - if( fid != NULL ) - memset( fid, 0, sizeof( *fid ) ); - - fibdsc.dsc$w_length = sizeof( fibdsc ); - fibdsc.dsc$a_pointer = (char *)&fib; - - for( bp = dp = dirnam; dp <= dirnam + dirlen; dp++) { - char ch; - - if( dp < dirnam + dirlen ) { - ch = *dp; - if( ch == '*' || ch == '%' ) { - return RMS$_WLD; - } - if( ch != '.' ) - continue; - } - len = (size_t)(dp - bp); - if( len == 0 ) - break; - - memcpy( nambuf, bp, len ); - namdsc.dsc$w_length = (uint16_t)(len + sizeof( ".DIR;1" ) -1); - memcpy( nambuf+len, ".DIR;1", namdsc.dsc$w_length ); - - namdsc.dsc$a_pointer = nambuf; - - dir = cache_find( (void *) &vcb->dircache, namdsc.dsc$w_length, nambuf, - &sts, dircmp, dir_create ); - if( dir == NULL ) { - return RMS$_DNF; - } - cache_untouch( &dir->cache, FALSE ); - - if( $MATCHCOND( sts, SS$_CREATED ) ) { - memset( &fib, 0, sizeof( fib ) ); - memcpy( &fib.fib$w_did_num, &srcdir, sizeof( struct fiddef ) ); - - sts = direct( vcb, &fibdsc, &namdsc, NULL, NULL, DIRECT_FIND ); - if( $FAILED(sts) ) { - cache_delete( &dir->cache ); - return RMS$_DNF; - } - dir->parent = srcdir; - dir->record.dir$w_verlimit = fib.fib$w_verlimit; - memcpy( &dir->entry.dir$w_fid, &fib.fib$w_fid_num, sizeof( struct fiddef ) ); - } - if( (dp + 2 < dirnam + dirlen) && dp[1] == '.' && dp[2] == '.' ) { - return RMS$_DNF; - } - srcdir = dir->entry.dir$w_fid; - bp = dp +1; - } - if( dir != NULL ) { - if( dirid ) - *dirid = dir->parent; - if( fid ) - *fid = dir->entry.dir$w_fid; - return SS$_NORMAL; - } - return RMS$_DNF; -} +/* Direct.c */ + +/* + * 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +/* 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 +#include +#include +#include +#include +#include + +#include "access.h" +#include "descrip.h" +#include "direct.h" +#include "fibdef.h" +#include "ods2.h" +#include "ssdef.h" +#include "stsdef.h" +#include "sysmsg.h" + +#define BLOCKSIZE 512 +#define MAXREC (BLOCKSIZE - 2) + +/* Some statistical counters... */ + +static int direct_lookups = 0; +static int direct_searches = 0; +static int direct_deletes = 0; +static int direct_inserts = 0; +static int direct_splits = 0; +static int direct_checks = 0; +static int direct_matches = 0; + +/************************************************************** direct_show() */ + +/* direct_show - to print directory statistics */ + +void direct_show( void ) { + printf( "DIRECT_SHOW Lookups: %d ", direct_lookups ); + printf( "Searches: %d ", direct_searches ); + printf( "Deletes: %d ", direct_deletes ); + printf( "Inserts: %d ", direct_inserts ); + printf( "Splits: %d\n", direct_splits ); +} + +/*************************************************************** name_check() */ + +/* name_check() - take a name specification and return name length without + the version number, an integer version number, and a wildcard flag */ + +static vmscond_t name_check( char *str, int len, int *retlen, + int *retver, int *wildflag ) { + int wildcard = FALSE; + char *name_start = str; + register int dots = 0; + register char *name = name_start; + register char *name_end = name + len; + direct_checks++; + + /* Go through the specification checking for illegal characters */ + + while (name < name_end) { + register char ch = *name++; + if (ch == '.') { + if ((name - name_start) > 40) + return SS$_BADFILENAME; + name_start = name; + if (dots++ > 1) + break; + } else { + if (ch == ';') { + break; + } else { + if (ch == '*' || ch == '%') { + wildcard = TRUE; + } else { + if (ch == '[' || ch == ']' || ch == ':' || + !isprint(ch)) + return SS$_BADFILENAME; + } + } + } + } + if ((name - name_start) > 40) + return SS$_BADFILENAME; + + /* Return the name length and start checking the version */ + + *retlen = (int)(name - str - 1); + dots = 0; + if (name < name_end) { + register char ch = *name; + if (ch == '*') { + if (++name < name_end) + return SS$_BADFILENAME; + dots = 32768; /* Wildcard representation of version! */ + wildcard = TRUE; + } else { + register int sign = 1; + if (ch == '-') { + name++; + sign = -1; + } + while (name < name_end) { + ch = *name++; + if (!isdigit(ch)) + return SS$_BADFILENAME; + dots = dots * 10 + (ch - '0'); + } + dots *= sign; + } + } + *retver = dots; + *wildflag = wildcard; + return SS$_NORMAL; +} + +/*************************************************************** name_match() */ + +#define MAT_LT 0 +#define MAT_EQ 1 +#define MAT_GT 2 +#define MAT_NE 3 + +/* name_match() - compare a name specification with a directory entry + and determine if there is a match, too big, too small... */ + +int name_match(char *spec, int spec_len, char *dirent, int dirent_len) { + int percent = MAT_GT; + register char *name = spec, *entry = dirent; + register char *name_end = name + spec_len, *entry_end = entry + dirent_len; + direct_matches++; + + /* See how much name matches without wildcards... */ + + while (name < name_end && entry < entry_end) { + register char sch = *name; + if (sch != '*') { + register char ech = *entry; + if (sch != ech) { + if (toupper(sch) != toupper(ech)) { + if (sch == '%') { + percent = MAT_NE; + } else { + break; + } + } + } + } else { + break; + } + name++; + entry++; + } + + /* Mismatch - return result unless wildcard... */ + + if (name >= name_end) { + if (entry >= entry_end) { + return MAT_EQ; + } else { + return percent; + } + } else { + /* See if we can find a match with wildcards */ + + if (*name != '*') { + if (percent == MAT_NE) + return MAT_NE; + if (entry < entry_end) + if (toupper(*entry) > toupper(*name)) + return MAT_GT; + return MAT_LT; + } + /* Strip out wildcard(s) - if end then we match! */ + + do { + name++; + } while (name < name_end && *name == '*'); + if (name >= name_end) + return MAT_EQ; + + /* Proceed to examine the specification past the wildcard... */ + + while (name < name_end) { + register int offset = 1; + register char fch = toupper(*name++); + + /* See if can can find a match for the first character... */ + + if (fch != '%') { + while (entry < entry_end) { + if (toupper(*entry) != fch) { + entry++; + } else { + break; + } + } + } + /* Give up if we can't find that one lousy character... */ + + if (entry >= entry_end) + return MAT_NE; + entry++; + + /* See how much of the rest we can match... */ + + while (name < name_end && entry < entry_end) { + register char sch = *name, ech; + if (sch == '*') + break; + if (sch != (ech = *entry)) + if (toupper(sch) != toupper(ech)) + if (sch != '%') + break; + name++; + entry++; + offset++; + } + + /* If matching died because of a wildcard we are OK... */ + + if (name < name_end && *name == '*') { + do { + name++; + } while (name < name_end && *name == '*'); + if (name >= name_end) + return MAT_EQ; + + /* Otherwise we finished OK or we need to try again... */ + + } else { + if (name >= name_end && entry >= entry_end) + return MAT_EQ; + name -= offset; + entry -= (size_t)offset - 1; + } + } + } + + /* No more specification - match depends on remainder of entry... */ + + if (entry < entry_end) + return MAT_NE; + return MAT_EQ; +} + +/*************************************************************** insert_ent() */ + +/* insert_ent() - procedure to add a directory entry at record dr entry de */ + +vmscond_t insert_ent( struct FCB * fcb, uint32_t eofblk, uint32_t curblk, + struct VIOC * vioc, char *buffer, + struct dir$r_rec * dr, struct dir$r_ent * de, + char *filename, uint32_t filelen, + uint32_t version, struct fiddef * fid, + uint16_t *reslen, struct dsc$descriptor *resdsc ) { + register vmscond_t sts = SS$_NORMAL; + register size_t inuse = 0; + register uint16_t addlen; + uint16_t verlimit; + size_t avail = 0, len; + char *p, vbuf[sizeof(";65535")]; + + verlimit = version >> 16; + version &= 0xffff; + + /* Return result */ + + if( resdsc != NULL && resdsc->dsc$w_length != 0 ) { + avail = resdsc->dsc$w_length; + p = resdsc->dsc$a_pointer; + len = filelen; + if( len > avail ) + len = avail; + memcpy( p, filename, len ); + avail -= len; + p += len; + len = snprintf( vbuf, sizeof( vbuf ), ";%u", version ); + if( len > avail ) + len = avail; + memcpy( p, vbuf, len ); + p += len; + *reslen = (unsigned short)(p - resdsc->dsc$a_pointer); + } + + /* Compute space required... */ + + addlen = sizeof(struct dir$r_ent); + direct_inserts++; + if (de == NULL) + addlen += (((size_t)filelen + 1) & ~1) + offsetof(struct dir$r_rec, dir$t_name); + + /* Compute block space in use ... */ + + { + int invalid_dir = TRUE; + while (TRUE) { + register size_t reclen; + register struct dir$r_rec *nr; + + nr = (struct dir$r_rec *) (buffer + inuse); + + if( dr == nr ) + invalid_dir = FALSE; + reclen = F11WORD(nr->dir$w_size); + if (reclen == 0xffff) /* End of data marker */ + break; + reclen += 2; + inuse += reclen; + reclen -= (((size_t)nr->dir$b_namecount + 1) & ~1) + offsetof(struct dir$r_rec, dir$t_name); + if (inuse > MAXREC || (inuse & 1) || reclen <= 0 || + reclen % 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 (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); + } + } + } + + /* If not enough space free extend the directory... */ + + if (addlen > MAXREC - inuse) { + register struct dir$r_rec *nr; + unsigned keep_new = 0; + char *newbuf; + struct VIOC *newvioc; + uint32_t newblk = eofblk + 1; + + direct_splits++; +#if DEBUG + printf( "Splitting directory 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' ); +#endif + if( newblk > fcb->hiblock ) { +#if DEBUG + printf("Extending directory during split\n"); +#endif + if( $FAILS(sts = update_extend( fcb, newblk - fcb->hiblock, 0 )) ) { +#if DEBUG + printf( "Failed to extend a directory during a split\n" ); +#endif + return $SETLEVEL(SS$_DIRALLOC, SEVERE); + } + } + + if( fcb->highwater != 0 && newblk > fcb->highwater -1 ) { + fcb->highwater = newblk + 1; + fcb->head->fh2$l_highwater = F11LONG( newblk + 1 ); + } + + if( $SUCCESSFUL(sts = accesschunk( fcb, newblk, &newvioc, &newbuf, NULL, 1 )) ) { + while( newblk > curblk + 1 ) { + char *frombuf; + struct VIOC *fromvioc; + + if( $FAILED(sts = accesschunk( fcb, newblk - 1, &fromvioc, &frombuf, NULL, 1 )) ) + break; + memcpy( newbuf, frombuf, BLOCKSIZE ); + sts = deaccesschunk( newvioc, newblk, 1, TRUE); + newvioc = fromvioc; + newbuf = frombuf; + newblk--; + if( $FAILED(sts) ) + break; + } + } else { + newvioc = NULL; + } + if( $FAILED(sts) ) { + if( newvioc != NULL ) + deaccesschunk( newvioc, 0, 0, FALSE); + deaccesschunk( vioc, 0, 0, FALSE ); + return sts; + } + memset( newbuf, 0, BLOCKSIZE ); + eofblk++; + fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP( eofblk + 1 ); + + /* First find where the next record is... */ + + nr = dr; + 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 || + F11WORD(nr->dir$w_size) <= MAXREC) { + register struct dir$r_rec *sp; + + 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$w_size = F11WORD(0xffff); + if (sp == dr && + (de != NULL || (char *) sp >= buffer + MAXREC - addlen)) { + if (de != NULL) + de = (struct dir$r_ent *) + (newbuf + ((char *) de - (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 uint32_t reclen; + register struct dir$r_rec *nbr; + + reclen = ( (((size_t)dr->dir$b_namecount + 1) & ~1) + + offsetof(struct dir$r_rec, dir$t_name) ); + nbr = (struct dir$r_rec *) newbuf; +#if DEBUG + 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); +#endif + memcpy(newbuf, buffer, reclen); + memcpy(newbuf + reclen, de, ((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$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$r_rec *) newbuf; + de = (struct dir$r_ent *) (newbuf + reclen); + keep_new = 1; + } + } + + /* Need to decide which buffer we are going to keep (to write to) */ + + if (keep_new) { + sts = deaccesschunk( vioc, curblk, 1, TRUE); + curblk = newblk; + vioc = newvioc; + buffer = newbuf; + } else { + sts = deaccesschunk( newvioc, newblk, 1, TRUE ); + } + if( $FAILED(sts) ) + printf("I/O error splitting directory record: %s\n", getmsg(sts, 0)); + } + /* After that we can just add the record or entry as appropriate... */ + + if (de == NULL) { + memmove((char *) dr + addlen, dr, + BLOCKSIZE - (((char *) dr + addlen) - buffer)); + dr->dir$w_size = F11WORD(addlen - 2); + dr->dir$w_verlimit = verlimit; + 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$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$w_version = F11WORD(version); + fid_copy( &de->dir$w_fid, fid, 0); + return deaccesschunk( vioc, curblk, 1, TRUE ); +} + +/*************************************************************** delete_ent() */ + +/* delete_ent() - delete a directory entry */ + +vmscond_t delete_ent( struct FCB * fcb, struct VIOC * vioc, uint32_t curblk, + struct dir$r_rec * dr, struct dir$r_ent * de, + char *buffer, uint32_t eofblk ) { + vmscond_t sts = SS$_NORMAL; + uint32_t ent; + + direct_deletes++; + ent = ((size_t)F11WORD(dr->dir$w_size) + 2 - offsetof(struct dir$r_rec, dir$t_name) + - (((size_t)dr->dir$b_namecount + 1) & ~1)) / sizeof(struct dir$r_ent); + if (ent > 1) { + char *ne; + + ne = (char *) de + sizeof(struct dir$r_ent); + memmove( de, ne, BLOCKSIZE - (ne - buffer) ); + dr->dir$w_size = F11WORD(F11WORD(dr->dir$w_size) - sizeof(struct dir$r_ent)); + } else { + char *nr; + + nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; + if( eofblk == 1 || (char *) dr > buffer || + (nr <= buffer + MAXREC && (unsigned short) *nr < BLOCKSIZE) ) { + memmove(dr, nr, BLOCKSIZE - (nr - buffer)); + } else { + while (curblk < eofblk) { + char *nxtbuffer; + struct VIOC *nxtvioc; + + sts = accesschunk(fcb, curblk + 1, &nxtvioc, &nxtbuffer, NULL, 1); + if( $FAILED(sts) ) + break; + memcpy(buffer, nxtbuffer, BLOCKSIZE); + if( $FAILS(sts = deaccesschunk(vioc, curblk++, 1, TRUE)) ) + break; + buffer = nxtbuffer; + vioc = nxtvioc; + } + if( $SUCCESSFUL(sts) ) { + fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP(eofblk); + eofblk--; + } + } + } + { + vmscond_t retsts; + + retsts = deaccesschunk(vioc, curblk, 1, TRUE); + if( $SUCCESSFUL(sts) ) + sts = retsts; + return sts; + } +} + +/*************************************************************** return_ent() */ + +/* return_ent() - return information about a directory entry */ + +vmscond_t return_ent(struct FCB * fcb, struct VIOC * vioc, uint32_t curblk, + struct dir$r_rec * dr, struct dir$r_ent * de, struct fibdef * fib, + uint16_t *reslen, struct dsc_descriptor * resdsc, + int wildcard) { + if( resdsc != NULL ) { + register uint32_t scale = 10; + register uint16_t version; + register size_t length; + register char *ptr; + register size_t outlen; + + version = F11WORD(de->dir$w_version); + length = dr->dir$b_namecount; + ptr = resdsc->dsc_a_pointer; + outlen = resdsc->dsc_w_length; + + if (length > outlen) + length = outlen; + memcpy(ptr, dr->dir$t_name, length); + while( version >= scale ) + scale *= 10; + ptr += length; + if( length < outlen ) { + *ptr++ = ';'; + length++; + do { + if( length >= outlen ) + break; + scale /= 10; + *ptr++ = version / scale + '0'; + version %= scale; + length++; + } while( scale > 1 ); + } + *reslen = (uint16_t)length; + } + 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 { + fib->fib$l_wcc = 0; + } + return deaccesschunk(vioc, 0, 0, TRUE); +} + +/*************************************************************** search_ent() */ + +/* search_ent() - search for a directory entry */ + +vmscond_t search_ent( struct FCB *fcb, + struct dsc_descriptor *fibdsc, + struct dsc_descriptor *filedsc, uint16_t *reslen, + struct dsc_descriptor *resdsc, uint32_t eofblk, + unsigned action ) { + register vmscond_t sts; + uint32_t curblk; + struct VIOC *vioc = NULL; + char *searchspec, *buffer; + int searchlen, version, wildcard, wcc_flag; + struct fibdef *fib; + + fib = (struct fibdef *) fibdsc->dsc_a_pointer; + direct_lookups++; + + /* 1) Generate start block (wcc gives start point) + * 2) Search for start + * 3) Scan until found or too big or end. + */ + + curblk = fib->fib$l_wcc; + if (curblk != 0) { + searchspec = resdsc->dsc_a_pointer; + sts = name_check( searchspec, *reslen, &searchlen, &version, &wildcard ); + if( MODIFIES(action) || wildcard ) + sts = RMS$_WLD; + wcc_flag = TRUE; + } else { + searchspec = filedsc->dsc_a_pointer; + sts = name_check( searchspec, filedsc->dsc_w_length, &searchlen, &version, + &wildcard); + if( (MODIFIES(action) && wildcard) || (action == DIRECT_CREATE && version < 0) ) { + sts = RMS$_WLD; + } + wcc_flag = FALSE; + } + if( $FAILED(sts) ) + return sts; + + /* Identify starting block...*/ + + if (*searchspec == '*' || *searchspec == '%') { + curblk = 1; + } else { + register uint32_t loblk = 1, hiblk = eofblk; + if( curblk < 1 || curblk > eofblk ) + curblk = (eofblk + 1) / 2; + + while (loblk < hiblk) { + register int cmp; + register uint32_t newblk; + register struct dir$r_rec *dr; + direct_searches++; + + if( $FAILS(sts = accesschunk( fcb, curblk, &vioc, &buffer, NULL, MODIFIES(action) )) ) + return sts; + dr = (struct dir$r_rec *) buffer; + if( F11WORD(dr->dir$w_size) > MAXREC ) { + cmp = MAT_GT; + } else { + 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$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 (F11WORD(de->dir$w_version) > version) { + cmp = MAT_LT; /* further ahead... */ + } + } + } + } + } + switch (cmp) { + case MAT_LT: + if (curblk == fib->fib$l_wcc) { + newblk = hiblk = loblk = curblk; + } else { + loblk = curblk; + newblk = (loblk + hiblk + 1) / 2; + } + break; + case MAT_GT: + case MAT_NE: + newblk = (loblk + curblk) / 2; + hiblk = curblk - 1; + break; + default: + newblk = hiblk = loblk = curblk; + } + if (newblk != curblk) { + if( $FAILS(sts = deaccesschunk( vioc, 0, 0, TRUE )) ) + return sts; + vioc = NULL; + curblk = newblk; + } + } + } + + /* Now to read sequentially to find entry... */ + + { + char last_name[80]; + unsigned last_len = 0; + register int relver = 0; + while( $SUCCESSFUL(sts) && curblk <= eofblk ) { + 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 ) { + if( $FAILS(sts = accesschunk( fcb, curblk, &vioc, + &buffer, NULL, MODIFIES(action) )) ) + return sts; + } + 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$t_name, last_len) != + MAT_EQ ) { + relver = 0; + } + } + + /* Now loop through the records seeing which match our spec... */ + + while (TRUE) { /* dr records within block */ + register char *nr = (char *) dr + F11WORD(dr->dir$w_size) + 2; + if (nr >= buffer + BLOCKSIZE) + break; + 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; + if( $FAILS(sts = name_check(searchspec, filedsc->dsc_w_length, + &searchlen, &version, &wildcard)) ) + break; + } else { + if (cmp == MAT_EQ) { + 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 = RMS$_VER; + break; + } + } + /* Look at each directory entry to see + if it is what we want... */ + + if ((char *) dr != buffer) relver = 0; + cmp = MAT_LT; + while ((char *) de < nr) { + if ((version < 1) ? + (relver > version) : + (version < F11WORD(de->dir$w_version))) { + relver--; + de++; + } else { + if (version > 32767 || version == relver || + version == F11WORD(de->dir$w_version)) { + cmp = MAT_EQ; + } else { + cmp = MAT_GT; + } + if (!wcc_flag) { + break; + } else { + wcc_flag = FALSE; + searchspec = filedsc->dsc_a_pointer; + if( $FAILS(sts = name_check( searchspec, + filedsc->dsc_w_length, + &searchlen, &version, + &wildcard )) ) + break; + if (name_match(searchspec, searchlen, + dr->dir$t_name, + dr->dir$b_namecount) != + MAT_EQ) { + cmp = MAT_NE; + break; + } + if (version < 0) { + relver = -32768; + cmp = MAT_GT; + break; + } + if (cmp == MAT_EQ) { + relver--; + de++; + } + cmp = MAT_LT; + } + } + } + if( $FAILED(sts) ) + break; + + /* Decide what to do with the entry we have found... */ + + if (cmp == MAT_EQ) { + switch (action) { + 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 == DIRECT_CREATE ) { + return insert_ent( fcb, eofblk, curblk, vioc, buffer, + dr, de, searchspec, searchlen, + (fib->fib$w_verlimit << 16) | version, + (struct fiddef *)&fib->fib$w_fid_num, reslen, resdsc ); + + } + } + } + /* Finish unless we expect more... */ + + if (cmp == MAT_GT && !wildcard) + break; + + /* If this is the last record in the block store the name + so that if it continues into the next block we can get + the relative version right! Sigh! */ + + 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$t_name, last_len); + dr = (struct dir$r_rec *) nr; + break; + } + 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( $FAILED(sts) || action != DIRECT_CREATE || + (cmp != MAT_GT && curblk < eofblk)) { + register vmscond_t dests; + + if( $FAILS(dests = deaccesschunk( vioc, 0, 0, TRUE )) ) { + sts = dests; + break; + } + vioc = NULL; + curblk++; + } else { + if( version == 0 ) + version = 1; + return insert_ent( fcb, eofblk, curblk, vioc, buffer, dr, NULL, + searchspec, searchlen, (fib->fib$w_verlimit << 16) | version, + (struct fiddef *)&fib->fib$w_fid_num, + reslen, resdsc ); + } + } /* curblk blocks within file */ + } + + /* We achieved nothing! Report the failure... */ + + if( $SUCCESSFUL(sts) ) { + fib->fib$l_wcc = 0; + if (wcc_flag || wildcard) { + sts = SS$_NOMOREFILES; + } else { + sts = SS$_NOSUCHFILE; + } + } + return sts; +} + +/******************************************************************* direct() */ + +/* direct() - this routine handles all directory manipulations:- + action 0 - find directory entry + 1 - delete entry + 2 - create an entry + */ + +unsigned direct( struct VCB * vcb, struct dsc_descriptor * fibdsc, + struct dsc_descriptor * filedsc, uint16_t *reslen, + struct dsc_descriptor * resdsc, unsigned action ) { + struct FCB *fcb; + register vmscond_t sts; + uint32_t eofblk; + register struct fibdef *fib; + + fib = (struct fibdef *) fibdsc->dsc_a_pointer; + + if( $SUCCESSFUL(sts = accessfile( vcb, (struct fiddef *) &fib->fib$w_did_num, + &fcb, MODIFIES(action) )) ) { + 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; + if( eofblk == 0 ) { + struct VIOC *vioc; + char *buffer; + uint32_t blocks; + + if( action != DIRECT_CREATE ) { + int searchlen, version, wildcard; + + deaccessfile( fcb ); + if( fib->fib$l_wcc ) { + if( $FAILS(sts = name_check( resdsc->dsc$a_pointer, *reslen, + &searchlen, &version, &wildcard )) ) + return sts; + fib->fib$l_wcc = 0; + return SS$_NOMOREFILES; + } + if( $FAILS(sts = name_check( filedsc->dsc$a_pointer, + filedsc->dsc$w_length, + &searchlen, &version, &wildcard )) ) + return sts; + if( wildcard ) + return SS$_NOMOREFILES; + return SS$_NOSUCHFILE; + } + + eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); + if( eofblk == 0 ) { + if( $FAILS(sts = update_extend( fcb, 1, 0 )) ) { + deaccessfile( fcb ); + return sts; + } + eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); + if( fcb->highwater != 0 ) { + fcb->highwater = 2; + fcb->head->fh2$l_highwater = F11SWAP( 2 ); + } + } + fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP( 2 ); + fcb->head->fh2$w_recattr.fat$w_ffbyte = 0; + eofblk = 1; + if( $FAILS(sts = accesschunk( fcb, 1, &vioc, &buffer, &blocks, 1 )) ) { + deaccessfile( fcb ); + return sts; + } + memset( buffer, 0, 512 ); + ((f11word *)buffer)[0] = 0xffff; + deaccesschunk( vioc, 1, blocks, TRUE ); + } + sts = search_ent( fcb, fibdsc, filedsc, reslen, resdsc, eofblk, action ); + } else { + sts = SS$_BADIRECTORY; + } + { + register uint32_t dests; + + dests = deaccessfile( fcb ); + if( $SUCCESSFUL(sts) ) + sts = dests; + } + } + if( MODIFIES(action) ) { + cache_flush(); + while( vcb->dircache ) /* Too conservative...but safe */ + cache_delete( (struct CACHE *)vcb->dircache ); + } + + return sts; +} + +/******************************************************************* dircmp() */ + +/* Function to compare directory cache names */ + +static int dircmp( uint32_t keylen, void *key, void *node ) { + register struct DIRCACHE *dirnode; + register int cmp; + unsigned dlen; + const char *kp, *dp; + int version, wild, namelen; + vmscond_t sts; + + if( $FAILS(sts = name_check(key, keylen, &namelen, &version, &wild) ) ) { + abort(); + } + + dirnode = (struct DIRCACHE *) node; + + if( version != F11WORD(dirnode->entry.dir$w_version) ) + return version - F11WORD(dirnode->entry.dir$w_version); + + dlen = dirnode->record.dir$b_namecount; + + kp = (const char *)key; + dp = dirnode->record.dir$t_name; + + while( keylen > 0 && dlen > 0 ) { + cmp = toupper(*kp++) - toupper(*dp++); + if( cmp != 0 ) + return cmp; + --keylen; + --dlen; + } + return keylen - dlen; +} + +/***************************************************************** dir_create() */ +static void *dir_create( uint32_t keylen, void *key, vmscond_t *retsts ) { + struct DIRCACHE *dir; + vmscond_t sts; + int version, wild, namelen; + + if( $FAILS(sts = name_check(key, keylen, &namelen, &version, &wild) ) ) { + *retsts = RMS$_DIR; + return NULL; + } + if( wild || version == 0 ) { + *retsts = SS$_NOSUCHFILE; + return NULL; + } + + if( (dir = (struct DIRCACHE *)calloc( 1, + offsetof( struct DIRCACHE, record.dir$t_name ) + + namelen + (namelen & 1) )) == NULL ) { + *retsts = SS$_INSFMEM; + return NULL; + } + + dir->cache.objtype = OBJTYPE_DIR; + dir->record.dir$w_size = (f11word)F11WORD( offsetof( struct dir$r_rec, + dir$t_name ) + namelen - 2); + dir->record.dir$b_namecount = (f11byte) namelen; + memcpy( dir->record.dir$t_name, key, namelen ); + dir->entry.dir$w_version = (f11word)F11WORD((f11word)version); + + *retsts = SS$_CREATED; + return dir; +} + +/***************************************************************** direct_dirid() */ + +/* Find FID of directory/ies using a cache. + * Used by sys$parse. Can be generalized... + * Input descriptor is a directory name string, including subdirectories. + * Output is the fid of the lowest level directory. + * Higher directories are searched (and cached). + * Any wildcards/recursion stop the search (they'll be resolved when a + * search matches them.) + */ + +vmscond_t direct_dirid( struct VCB *vcb, struct dsc$descriptor *dirdsc, + struct fiddef *dirid, struct fiddef *fid ) { + register struct DIRCACHE *dir = NULL; + vmscond_t sts; + struct fibdef fib; + struct dsc$descriptor fibdsc; + struct dsc$descriptor namdsc; + struct fiddef srcdir; + char *dirnam; + int dirlen; + size_t len; + char *bp, *dp; + char nambuf[256]; + + memset( &fib, 0, sizeof( fib ) ); + memset( &fibdsc, 0, sizeof( fibdsc ) ); + memset( &namdsc, 0, sizeof( namdsc ) ); + memset( &srcdir, 0, sizeof( srcdir ) ); + + dirlen = dirdsc->dsc$w_length; + + if( (size_t)dirlen > sizeof( nambuf ) -1 ) + abort(); + dirnam = dirdsc->dsc$a_pointer; + + srcdir.fid$w_num = FID$C_MFD; + srcdir.fid$w_seq = FID$C_MFD; + srcdir.fid$b_rvn = 0; + srcdir.fid$b_nmx = 0; + + if( dirlen < 1 || (dirlen == 6 && !memcmp( dirnam, "000000", 6 )) ) { + if( dirid ) + *dirid = srcdir; + if( fid ) + *fid = srcdir; + return SS$_NORMAL; + } + + if( dirid != NULL ) + memset( dirid, 0, sizeof( *dirid ) ); + if( fid != NULL ) + memset( fid, 0, sizeof( *fid ) ); + + fibdsc.dsc$w_length = sizeof( fibdsc ); + fibdsc.dsc$a_pointer = (char *)&fib; + + for( bp = dp = dirnam; dp <= dirnam + dirlen; dp++) { + char ch; + + if( dp < dirnam + dirlen ) { + ch = *dp; + if( ch == '*' || ch == '%' ) { + return RMS$_WLD; + } + if( ch != '.' ) + continue; + } + len = (size_t)(dp - bp); + if( len == 0 ) + break; + + memcpy( nambuf, bp, len ); + namdsc.dsc$w_length = (uint16_t)(len + sizeof( ".DIR;1" ) -1); + memcpy( nambuf+len, ".DIR;1", namdsc.dsc$w_length ); + + namdsc.dsc$a_pointer = nambuf; + + dir = cache_find( (void *) &vcb->dircache, namdsc.dsc$w_length, nambuf, + &sts, dircmp, dir_create ); + if( dir == NULL ) { + return RMS$_DNF; + } + cache_untouch( &dir->cache, FALSE ); + + if( $MATCHCOND( sts, SS$_CREATED ) ) { + memset( &fib, 0, sizeof( fib ) ); + memcpy( &fib.fib$w_did_num, &srcdir, sizeof( struct fiddef ) ); + + sts = direct( vcb, &fibdsc, &namdsc, NULL, NULL, DIRECT_FIND ); + if( $FAILED(sts) ) { + cache_delete( &dir->cache ); + return RMS$_DNF; + } + dir->parent = srcdir; + dir->record.dir$w_verlimit = fib.fib$w_verlimit; + memcpy( &dir->entry.dir$w_fid, &fib.fib$w_fid_num, sizeof( struct fiddef ) ); + } + if( (dp + 2 < dirnam + dirlen) && dp[1] == '.' && dp[2] == '.' ) { + return RMS$_DNF; + } + srcdir = dir->entry.dir$w_fid; + bp = dp +1; + } + if( dir != NULL ) { + if( dirid ) + *dirid = dir->parent; + if( fid ) + *fid = dir->entry.dir$w_fid; + return SS$_NORMAL; + } + return RMS$_DNF; +} diff --git a/extracters/ods2/direct.h b/extracters/ods2/direct.h index c7f62c4..c497bc2 100644 --- a/extracters/ods2/direct.h +++ b/extracters/ods2/direct.h @@ -1,39 +1,39 @@ -/* Direct.h Definitions for directory 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#ifndef _DIRECT_H -#define _DIRECT_H - -#include -#include "access.h" -#include "descrip.h" -#include "f11def.h" -#include "ods2.h" - -void direct_show( void ); -vmscond_t direct( struct VCB *vcb, struct dsc_descriptor *fibdsc, - struct dsc_descriptor *filedsc, uint16_t *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) - -vmscond_t direct_dirid( struct VCB *vcb, struct dsc$descriptor *dirdsc, - struct fiddef *dirid, struct fiddef *fid ); - -#endif /* #ifndef _DIRECT_H */ +/* Direct.h Definitions for directory 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#ifndef _DIRECT_H +#define _DIRECT_H + +#include +#include "access.h" +#include "descrip.h" +#include "f11def.h" +#include "ods2.h" + +void direct_show( void ); +vmscond_t direct( struct VCB *vcb, struct dsc_descriptor *fibdsc, + struct dsc_descriptor *filedsc, uint16_t *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) + +vmscond_t direct_dirid( struct VCB *vcb, struct dsc$descriptor *dirdsc, + struct fiddef *dirid, struct fiddef *fid ); + +#endif /* #ifndef _DIRECT_H */ diff --git a/extracters/ods2/dismountcmd.c b/extracters/ods2/dismountcmd.c index 1aa6b0f..f7af43a 100644 --- a/extracters/ods2/dismountcmd.c +++ b/extracters/ods2/dismountcmd.c @@ -1,64 +1,64 @@ -/* 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 contributions of the original author and - * subsequent contributors. This is free software; no - * warranty is offered, and while we believe it to be useful, - * you use it at your own risk. - */ - -#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() */ - -qual_t -dmoquals[] = { {"log", MOU_LOG, 0, NV, - "-commands dismount qual_log"}, - {"nolog", 0, MOU_LOG, NV, NULL }, - - {NULL, 0, 0, NV, NULL } -}; - -param_t -dmopars[] = { {"drive_name", REQ, STRING, NOPA, - "commands dismount drive"}, - - {NULL, 0, 0, NOPA, NULL} -}; - -DECL_CMD(dismount) { - vmscond_t sts; - options_t options; - struct DEV *dev; - - UNUSED(argc); - - if( $FAILS(sts = checkquals( &options, MOU_LOG, dmoquals, qualc, qualv )) ) { - return sts; - } - if( $SUCCESSFUL(sts = device_lookup( (uint32_t)strlen(argv[1]), - argv[1], FALSE, &dev )) ) { - device_done( dev ); - if( dev->vcb != NULL ) { - sts = dismount( dev->vcb, options ); - } else { - sts = SS$_DEVNOTMOUNT; - } - } - - return sts; -} +/* 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 contributions of the original author and + * subsequent contributors. This is free software; no + * warranty is offered, and while we believe it to be useful, + * you use it at your own risk. + */ + +#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() */ + +qual_t +dmoquals[] = { {"log", MOU_LOG, 0, NV, + "-commands dismount qual_log"}, + {"nolog", 0, MOU_LOG, NV, NULL }, + + {NULL, 0, 0, NV, NULL } +}; + +param_t +dmopars[] = { {"drive_name", REQ, STRING, NOPA, + "commands dismount drive"}, + + {NULL, 0, 0, NOPA, NULL} +}; + +DECL_CMD(dismount) { + vmscond_t sts; + options_t options; + struct DEV *dev; + + UNUSED(argc); + + if( $FAILS(sts = checkquals( &options, MOU_LOG, dmoquals, qualc, qualv )) ) { + return sts; + } + if( $SUCCESSFUL(sts = device_lookup( (uint32_t)strlen(argv[1]), + argv[1], FALSE, &dev )) ) { + device_done( dev ); + if( dev->vcb != NULL ) { + sts = dismount( dev->vcb, options ); + } else { + sts = SS$_DEVNOTMOUNT; + } + } + + return sts; +} diff --git a/extracters/ods2/en_us.msg b/extracters/ods2/en_us.msg index 87e30ae..4c10d43 100644 --- a/extracters/ods2/en_us.msg +++ b/extracters/ods2/en_us.msg @@ -1,748 +1,748 @@ -! All facilities defined here must be listed in the message compiler. -! -! This file defines all the ODS2-specific messages. The message compiler -! (a perl script, not the VMS utility) compiles this into a .h and mdf files for -! ODS2. Note that none of the facility codes chosen are registered with -! DEC :-). Also note that these are all customer facilites; they will not -! conflict with VMS facilities of the same name. Reconstructed messages from -! the VMS facilities are merged with this file when it is compiled. -! The comments in the (auto-generated) ssdef.h explain how to generate -! those sources. Only necessary if you are translating (I18n)... -! -! Copyright (C) 2016, 2022 Timothe Litt - litt at acm .org -! -! Provided as part of the ODS2 utility, 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 contributions of the original author and -! subsequent contributors. This is free software; no -! warranty is offered, and while we believe it to be useful, -! you use it at your own risk. This is free software; no -! warranty is offered, and while we believe it to be useful, -! you use it at your own risk. - -! ******************************************************************************** - .FACILITY ODS2,84 - .LITERAL ODS2_OFFSET = ODS2_FACILITY - ^X800 - - - .SEVERITY WARNING - - .LITERAL ODS2_NORMAL = (ODS2_OFFSET@16) + SHR_NORMAL + SHR_SUCCESS - .LITERAL ODS2_OPENIN = (ODS2_OFFSET@16) + SHR_OPENIN + SHR_WARN - - .SEVERITY SUCCESS - .BASE 200 - -CONFIRM_ALL -CONFIRM_YES -CONFIRM_NO /WARNING -CONFIRM_QUIT /WARNING - -EXIT - - - .SEVERITY INFO - .BASE 400 - -MSGFILE /FAO=1 -INVDEF /FAO=1 -HELPFILE /FAO=1 -HELPFILEVER1 /FAO=3 /IDENT=HELPFILEVER -HELPFILEVER2 /FAO=4 /IDENT=HELPFILEVER - - .SEVERITY WARNING - .BASE 600 - -FILHDRACC -SETCWD -LOCALE - - .SEVERITY ERROR - .BASE 800 - -AMBIGUOUS /FAO=2 -UNKNOWN /FAO=2 -NOVALUE /FAO=2 -VALUEREQ /FAO=2 -NOPAREN /FAO=2 -TOOMANY /FAO=1 -TOOFEW /FAO=1 - -MAXIND /FAO=1 -UNTERMSTR -INVNUMBER /FAO=1 -NOQUALS -NOQUALSOK /IDENT=NOQUALS -NOPAROK -INCONQUAL -BADPRO -BADUIC -BADVALUE - -OPENIND /FAO=1 -NOSHELL /FAO=1 -OSERROR /FAO=1 -OSERRORNO /FAO=1 /IDENT=OSERROR - -VMSTIME - -FCS2DV -BADORG -ILLDIRWRT - -FILESTRUCT /FAO=2 - - .SEVERITY SEVERE - .BASE 1000 - -INITIALFAIL - -BADMSGFILE -NOMSG /FAO_COUNT=1 - - .END - -! ******************************************************************************** - .FACILITY COPY,85 - .LITERAL COPY_OFFSET = COPY_FACILITY - ^X800 - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 - - .LITERAL COPY_COPIEDB = (COPY_OFFSET@16) + SHR_COPIEDB + SHR_INFO - .LITERAL COPY_COPIEDR = (COPY_OFFSET@16) + SHR_COPIEDR + SHR_INFO - -COPIEDU /IDENT=COPIED /FAO=3 - -CONFIRM /FAO=2 -COPIED /FAO=1 -NOFILES -DIRNOTCOPIED /FAO=1 - - .SEVERITY WARNING - .BASE 600 - - .LITERAL COPY_OPENIN = (COPY_OFFSET@16) + SHR_OPENIN + SHR_WARN - -PROTERR /FAO=1 -NOMATCH /FAO=1 - - .SEVERITY ERROR - .BASE 800 - - .LITERAL COPY_CLOSEOUT = (COPY_OFFSET@16) + SHR_CLOSEOUT + SHR_ERROR - .LITERAL COPY_OPENIN = (COPY_OFFSET@16) + SHR_OPENIN + SHR_ERROR - .LITERAL COPY_OPENOUT = (COPY_OFFSET@16) + SHR_OPENOUT + SHR_ERROR - .LITERAL COPY_PARSEFAIL = (COPY_OFFSET@16) + SHR_PARSEFAIL + SHR_ERROR - .LITERAL COPY_SEARCHFAIL = (COPY_OFFSET@16) + SHR_SEARCHFAIL + SHR_ERROR - .LITERAL COPY_READERR = (COPY_OFFSET@16) + SHR_READERR + SHR_ERROR - .LITERAL COPY_WRITEERR = (COPY_OFFSET@16) + SHR_WRITEERR + SHR_ERROR - .LITERAL COPY_INVALIDNL = (COPY_OFFSET@16) + SHR_INVALIDNL + SHR_ERROR - .LITERAL COPY_INVALIDVFU = (COPY_OFFSET@16) + SHR_INVALIDVFU + SHR_ERROR - .LITERAL COPY_INVALIDC0 = (COPY_OFFSET@16) + SHR_INVALIDC0 + SHR_ERROR - -NONAME - - .SEVERITY SEVERE - .BASE 1000 - -NOMEM /GAO=1 -GLOBABT /FAO=1 -GLOBERR /FAO=2 - - .END - -! ******************************************************************************** - .FACILITY CREATE,86 - .LITERAL CREATE_OFFSET = CREATE_FACILITY - ^X800 - - .SEVERITY INFO - .BASE 400 - .LITERAL CREATE_EXISTS = (CREATE_OFFSET@16) + SHR_EXISTS + SHR_INFO - .LITERAL CREATE_CREATED = (CREATE_OFFSET@16) + SHR_CREATED + SHR_INFO - -READY /FAO=1 -TERMEOF /FAO=1 - - .SEVERITY ERROR - .BASE 800 -CREDIRFAIL /FAO=1 - .LITERAL CREATE_NOTDIR = (CREATE_OFFSET@16) + SHR_NOTDIR + SHR_ERROR - .LITERAL CREATE_CLOSEOUT = (CREATE_OFFSET@16) + SHR_CLOSEOUT + SHR_ERROR - .LITERAL CREATE_INVALIDNL = (CREATE_OFFSET@16) + SHR_INVALIDNL + SHR_ERROR - .LITERAL CREATE_INVALIDVFU = (CREATE_OFFSET@16) + SHR_INVALIDVFU + SHR_ERROR - .LITERAL CREATE_INVALIDC0 = (CREATE_OFFSET@16) + SHR_INVALIDC0 + SHR_ERROR - - .END - -! ******************************************************************************** - .FACILITY DELETE,87 - .LITERAL DELETE_OFFSET = DELETE_FACILITY - ^X800 - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 - -CONFIRM /FAO:1 -DELETED /FAO=1 -NFILES /FAO=1 /IDENT=DELETED - - .SEVERITY WARNING - .BASE 600 - - .LITERAL DELETE_OPENIN = (DELETE_OFFSET@16) + SHR_OPENIN + SHR_WARN - -NOFILES -NOTDELETED /FAO=1 -DIRNOTEMPTY - - .SEVERITY ERROR - .BASE 800 - - - .SEVERITY SEVERE - .BASE 1000 - - .END - -! ******************************************************************************** - .FACILITY DIFF,88 - .LITERAL DIFF_OFFSET = DIFF_FACILITY - ^X800 - - .SEVERITY SUCCESS - .BASE 200 - -NODIFF - - .SEVERITY INFO - .BASE 400 - -COMPARED /FAO=1 - - .SEVERITY WARNING - .BASE 600 - - .LITERAL DIFF_OPENIN = (DIFF_OFFSET@16) + SHR_OPENIN + SHR_ERROR - -DIFFER - - .END - -! ******************************************************************************** - .FACILITY DIRECT,89 - .LITERAL DIRECT_OFFSET = DIRECT_FACILITY - ^X800 - - .SEVERITY WARNING - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 -DIRHEAD /FAO=1 -FULLSIZE /FAO=1 -GRANDTOT /FAO=2 -FILETOTAL /FAO=1 -USEDTOT /FAO=1 -ALLOCTOT /FAO=1 -BOTHSIZE /FAO=2 -OWNER /FAO=2 -CREATED -REVISED -EXPIRES -BACKUP -FILEORG -SEQORG -RELORG -IDXORG -UNKORG /FAO=1 -FILEATT -ALLOC /FAO=1 -EXTEND <, Extend: !UW> /FAO=1 -GBC <, Global buffer count: !UW> /FAO=1 -VERLIMIT <, Version limit: !UW> /FAO=1 -DIRLIMIT <, Default version limit: !UW> /FAO=1 -NOVERLIMIT <, No version limit> -NODIRLIMIT <, No default version limit> - -CONTIG <, Contiguous> -CONTIGB <, Contiguous best try> -NOBACKUP <, Backups disabled> -DIRECTORY <, Directory file> -WRITEBACK <, Writeback> -READCHECK <, Read verification> -WRITECHECK <, Write verification> -LOCKED <, File locked> -SPOOL <, Spool file> -MARKDEL <, Marked for deletion> -BADBLOCK <, Contains bad block> - -EOFPOS /FAO=2 - -RECFMT -RECUDF -RECFIX /FAO=1 -RECVAR /FAO=1 -RECVFC /FAO=2 -RECSTM -RECSTMLF -RECSTMCR - -RECATT -RECATT_NONE -RECATT_FTN /FAO=1 -RECATT_CR /FAO=1 -RECATT_PRN /FAO=1 -RECATT_BLK /FAO=1 - -PROT_SYS -PROT_OWNER <, Owner:> -PROT_GROUP <, Group:> -PROT_WORLD <, World:> - -UIC < [!OW,!OW]> /FAO=2 - -IDNAME /FAO=1 -EXTNHDR /FAO=4 -HDROFFSETS /FAO=4 -STRUCLEVEL /FAO=2 -MAPSIZE /FAO=4 -MAPHIGHWATER /FAO=1 -MAPHEADER -MAPFMT0 /FAO=1 -MAPENTRY /FAO=5 -MAPFREE "!9UL <= First free" /FAO=1 - - .SEVERITY WARNING - .BASE 600 - - .LITERAL DIRECT_NOFILES = (DIRECT_OFFSET@16) + SHR_NOFILES + SHR_WARN - - .SEVERITY ERROR - .BASE 800 - - .LITERAL DIRECT_OPENOUT = (DIRECT_OFFSET@16) + SHR_OPENOUT + SHR_ERROR - - .SEVERITY SEVERE - .BASE 1000 - - .END - - -! ******************************************************************************** - .FACILITY DISM,90 - .LITERAL DISM_OFFSET = DISM_FACILITY - ^X800 - - .SEVERITY INFO - .BASE 400 - -DISMOUNTD /FAO=4 /IDENT=DISMOUNTED -DISMOUNTDV /FAO=2 /IDENT=DISMOUNTED - -DISMAL - .SEVERITY WARNING - .BASE 600 - -CANNOTDMT /FAO=2 - - .SEVERITY ERROR - .BASE 800 - - .SEVERITY SEVERE - .BASE 1000 - -DEVNOTDISM -USERFILES /FAO=1 - - .END - -! ******************************************************************************** - .FACILITY EXTEND,91 - .LITERAL EXTEND_OFFSET = EXTEND_FACILITY - ^X800 - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 - -NEWSIZE /FAO=1 - - .SEVERITY ERROR - .BASE 800 - -CONFLQUAL /FAO=2 - - .END - -! ******************************************************************************** - .FACILITY HELP,92 - .LITERAL HELP_OFFSET = HELP_FACILITY - ^X800 - - .SEVERITY INFO - .BASE 400 - -TERMWID /FAO=1 -TERMSIZE /FAO:2 -KEYWORD < keyword, one of the following:> -KEYWORDS < keywords, one or more of the following:> -QUALIFIER < qualifier, one of the following> -QUALIFIERS < qualifiers, one or more of the following> -LIST /FAO=1 -LISTS /FAO=1 -HEAD < Commands are:> -MORE < Additional information:> -EXPLAIN < Type HELP COMMAND or HELP TOPIC for information on any command or topic.!/ Type HELP HELP for information on the help system.> -NOCMDHELP /FAO=1 -NOPARHELP /FAO=1 -PARNOTACT -NOPARAMS /FAO=1 -PARHEADING -PARNOLIMIT < "*" indicates that a parameter can be used more than once.> -PAREXPLAIN < Type help !AZ PARAMETER for more about each parameter.> /FAO=1 -QUALHEAD - -SETVALUE - -SHOWCWD < Current working directory is !AZ> /FAO=1 -SHOWDEFQ < Default qualifiers for !AZ command> /FAO=1 -SHOWNODEFQ < None set> -SHOWQSTYLE < Qualifier style: !AZ> /FAO=1 -SHOWSTATS -SHOWVERON -SHOWVEROFF -SHOWVERS < !AZ !AZ built !AZ !AZ> /FAO=4 -SHOWVERSRL < !AZ !AZ built !AZ !AZ with libedit version !UL.!UL> /FAO=6 -SHOWVERSCSI < Direct SCSI access is supported> -SHOWVERNOVHD < VHD format image file support is not configured> -SHOWVERPLATFM < Platform: !AZ> /FAO=1 -SHOWVERCOMPLR < Compiler: !AZ, version !AZ> /FAO=2 -SHOWVLD < Visual Leak Detector is configured> -SHOWCMDHISTLN /FAO=2 -SHOWMSGPRINTED <(printed): > - - .SEVERITY WARNING - .BASE 600 - - .SEVERITY ERROR - .BASE 800 - -OPENHLP /FAO=1 -NOHELP /FAO=2 -HELPFMT /FAO=1 /IDENT=NOHELP -HELPLEVEL /FAO=1 -NOTEXT /FAO=2 -NOTEXT2 /FAO=1 /IDENT=HELPNOTEXT -SHOWNOCMDHIST -SETCMDHISTVAL - - .END -! ******************************************************************************** - .FACILITY INIT,93 - .LITERAL INIT_OFFSET = INIT_FACILITY - ^X800 - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 - -CONFIRM_PHY /FAO:1 /IDENT=CONFIRM -CONFIRM_VIRT /FAO:2 /IDENT=CONFIRM - -START_PHY /FAO=3 /IDENT=STARTING -START_VIRT /FAO=3 /IDENT=STARTNG - -VOLINIT /FAO=1 -SIZING1 /IDENT=SIZING /FAO=3 -SIZING2 /IDENT=SIZING /FAO=2 -PLACED1 /IDENT=SIZING /FAO=3 -PLACED2 < MFD at LBN !UL, index file at LBNs !UL thru !UL> /IDENT=SIZING /FAO=3 - -MFGBAD -SWBAD - - .SEVERITY WARNING - .BASE 600 - -INVOPT /FAO=2 - - .SEVERITY ERROR - .BASE 800 - -BADVOLLABEL -BADUSERNAME -BADMEDIUM /FAO=1 - - .SEVERITY SEVERE - .BASE 1000 - -TOOSMALL -NOHOMLOC -NOHOM2LOC /IDENT=NOHOMLOC - - .END - -! ******************************************************************************** - .FACILITY IO,94 - .LITERAL IO_OFFSET = IO_FACILITY - ^X800 - - .SEVERITY INFO - .BASE 400 - -STATS /FAO=3 -LARGEFILE "large files and devices (>2GB) are supported" -NOLARGEFILE "large files and devices (>2GB) are NOT supported" -UNXDEVHDR < Physical devices> -NODEVS - -NTDEVHEADER < Physical devices!/ Drv Type Physical Name!/ --- --------- -------------> -NTDEVREMOVABLE < !AZ Removable !AZ> /FAO=2 -NTDEVFIXED < !AZ Fixed !AZ> /FAO=2 -NTDEVREMOTE < !AZ Network> /FAO=1 -NTDEVCDROM < !AZ CDROM !AZ> /FAO=2 -NTDEVRAMDISK < !AZ RAMdisk !AZ> /FAO=2 -NTDEVOTHER < !AZ Other> /FAO=1 - -VNODEVS < No virtual devices are assigned> -VDEVHEADER < Virtual devices> - -VHDAVAIL -VHDNOTAVAIL -VHDFMTSTART /FAO=1 -VHDFMTEND -VMSMOUNT /FAO=1 -VMSDMT /FAO=1 - .SEVERITY WARNING - .BASE 600 - -VHDSAVED /FAO=1 - - .SEVERITY ERROR - .BASE 800 - -NOVHD -ISVIRTDEV /FAO=2 -VIRTDEVINUSE /FAO=2 -VIRTFILEINUSE /FAO=2 -BADPATH /FAO=1 -DIRRDERR /FAO=1 -LOCKVOL /FAO=1 -NOGEO /FAO=1 -OPENDEV /FAO=1 -READERR /FAO=1 -READLEN /FAO=3 -WRITELEN /FAO=3 -SEEKERR /FAO=1 -WRITEERR /FAO=1 -UNXNOTREG /FAO=1 -UNXNOTBLK /FAO=1 -EXISTS /FAO=1 -RDBLK2BIG /FAO=1 /IDENT=BLK2BIG -WRBLK2BIG /FAO=1 /IDENT=BLK2BIG -VHDONLY /FAO=1 -VHDSNAPFAIL /FAO=1 -VHDMEM -VHDCREATEERR /FAO=1 -VMSDEVSCAN -VMSNOMOUNT /FAO=1 -VMSNODMT /FAO=1 - - .SEVERITY SEVERE - .BASE 1000 - -OFFSET_TOO_BIG - -VHDNOLIB /FAO=1 -VHDNOSYM /FAO=2 -VHDNOTVALID /FAO=1 - - .END - -! ******************************************************************************** - .FACILITY MOUNT,95 - .LITERAL MOUNT_OFFSET = MOUNT_FACILITY - ^X800 - - .SEVERITY WARNING - - .SEVERITY SUCCESS - .BASE 200 - - .SEVERITY INFO - .BASE 400 -MOUNTED /FAO=3 -SNAPOK /FAO=2 -VSMOUNTED /FAO=1 -WRITELCK /FAO=1 - - .SEVERITY WARNING - .BASE 600 - -BADSCB /FAO=1 - - .SEVERITY ERROR - .BASE 800 - -BADLABEL /FAO=1 +TERMSIZE /FAO:2 +KEYWORD < keyword, one of the following:> +KEYWORDS < keywords, one or more of the following:> +QUALIFIER < qualifier, one of the following> +QUALIFIERS < qualifiers, one or more of the following> +LIST /FAO=1 +LISTS /FAO=1 +HEAD < Commands are:> +MORE < Additional information:> +EXPLAIN < Type HELP COMMAND or HELP TOPIC for information on any command or topic.!/ Type HELP HELP for information on the help system.> +NOCMDHELP /FAO=1 +NOPARHELP /FAO=1 +PARNOTACT +NOPARAMS /FAO=1 +PARHEADING +PARNOLIMIT < "*" indicates that a parameter can be used more than once.> +PAREXPLAIN < Type help !AZ PARAMETER for more about each parameter.> /FAO=1 +QUALHEAD + +SETVALUE + +SHOWCWD < Current working directory is !AZ> /FAO=1 +SHOWDEFQ < Default qualifiers for !AZ command> /FAO=1 +SHOWNODEFQ < None set> +SHOWQSTYLE < Qualifier style: !AZ> /FAO=1 +SHOWSTATS +SHOWVERON +SHOWVEROFF +SHOWVERS < !AZ !AZ built !AZ !AZ> /FAO=4 +SHOWVERSRL < !AZ !AZ built !AZ !AZ with libedit version !UL.!UL> /FAO=6 +SHOWVERSCSI < Direct SCSI access is supported> +SHOWVERNOVHD < VHD format image file support is not configured> +SHOWVERPLATFM < Platform: !AZ> /FAO=1 +SHOWVERCOMPLR < Compiler: !AZ, version !AZ> /FAO=2 +SHOWVLD < Visual Leak Detector is configured> +SHOWCMDHISTLN /FAO=2 +SHOWMSGPRINTED <(printed): > + + .SEVERITY WARNING + .BASE 600 + + .SEVERITY ERROR + .BASE 800 + +OPENHLP /FAO=1 +NOHELP /FAO=2 +HELPFMT /FAO=1 /IDENT=NOHELP +HELPLEVEL /FAO=1 +NOTEXT /FAO=2 +NOTEXT2 /FAO=1 /IDENT=HELPNOTEXT +SHOWNOCMDHIST +SETCMDHISTVAL + + .END +! ******************************************************************************** + .FACILITY INIT,93 + .LITERAL INIT_OFFSET = INIT_FACILITY - ^X800 + + .SEVERITY SUCCESS + .BASE 200 + + .SEVERITY INFO + .BASE 400 + +CONFIRM_PHY /FAO:1 /IDENT=CONFIRM +CONFIRM_VIRT /FAO:2 /IDENT=CONFIRM + +START_PHY /FAO=3 /IDENT=STARTING +START_VIRT /FAO=3 /IDENT=STARTNG + +VOLINIT /FAO=1 +SIZING1 /IDENT=SIZING /FAO=3 +SIZING2 /IDENT=SIZING /FAO=2 +PLACED1 /IDENT=SIZING /FAO=3 +PLACED2 < MFD at LBN !UL, index file at LBNs !UL thru !UL> /IDENT=SIZING /FAO=3 + +MFGBAD +SWBAD + + .SEVERITY WARNING + .BASE 600 + +INVOPT /FAO=2 + + .SEVERITY ERROR + .BASE 800 + +BADVOLLABEL +BADUSERNAME +BADMEDIUM /FAO=1 + + .SEVERITY SEVERE + .BASE 1000 + +TOOSMALL +NOHOMLOC +NOHOM2LOC /IDENT=NOHOMLOC + + .END + +! ******************************************************************************** + .FACILITY IO,94 + .LITERAL IO_OFFSET = IO_FACILITY - ^X800 + + .SEVERITY INFO + .BASE 400 + +STATS /FAO=3 +LARGEFILE "large files and devices (>2GB) are supported" +NOLARGEFILE "large files and devices (>2GB) are NOT supported" +UNXDEVHDR < Physical devices> +NODEVS + +NTDEVHEADER < Physical devices!/ Drv Type Physical Name!/ --- --------- -------------> +NTDEVREMOVABLE < !AZ Removable !AZ> /FAO=2 +NTDEVFIXED < !AZ Fixed !AZ> /FAO=2 +NTDEVREMOTE < !AZ Network> /FAO=1 +NTDEVCDROM < !AZ CDROM !AZ> /FAO=2 +NTDEVRAMDISK < !AZ RAMdisk !AZ> /FAO=2 +NTDEVOTHER < !AZ Other> /FAO=1 + +VNODEVS < No virtual devices are assigned> +VDEVHEADER < Virtual devices> + +VHDAVAIL +VHDNOTAVAIL +VHDFMTSTART /FAO=1 +VHDFMTEND +VMSMOUNT /FAO=1 +VMSDMT /FAO=1 + .SEVERITY WARNING + .BASE 600 + +VHDSAVED /FAO=1 + + .SEVERITY ERROR + .BASE 800 + +NOVHD +ISVIRTDEV /FAO=2 +VIRTDEVINUSE /FAO=2 +VIRTFILEINUSE /FAO=2 +BADPATH /FAO=1 +DIRRDERR /FAO=1 +LOCKVOL /FAO=1 +NOGEO /FAO=1 +OPENDEV /FAO=1 +READERR /FAO=1 +READLEN /FAO=3 +WRITELEN /FAO=3 +SEEKERR /FAO=1 +WRITEERR /FAO=1 +UNXNOTREG /FAO=1 +UNXNOTBLK /FAO=1 +EXISTS /FAO=1 +RDBLK2BIG /FAO=1 /IDENT=BLK2BIG +WRBLK2BIG /FAO=1 /IDENT=BLK2BIG +VHDONLY /FAO=1 +VHDSNAPFAIL /FAO=1 +VHDMEM +VHDCREATEERR /FAO=1 +VMSDEVSCAN +VMSNOMOUNT /FAO=1 +VMSNODMT /FAO=1 + + .SEVERITY SEVERE + .BASE 1000 + +OFFSET_TOO_BIG + +VHDNOLIB /FAO=1 +VHDNOSYM /FAO=2 +VHDNOTVALID /FAO=1 + + .END + +! ******************************************************************************** + .FACILITY MOUNT,95 + .LITERAL MOUNT_OFFSET = MOUNT_FACILITY - ^X800 + + .SEVERITY WARNING + + .SEVERITY SUCCESS + .BASE 200 + + .SEVERITY INFO + .BASE 400 +MOUNTED /FAO=3 +SNAPOK /FAO=2 +VSMOUNTED /FAO=1 +WRITELCK /FAO=1 + + .SEVERITY WARNING + .BASE 600 + +BADSCB /FAO=1 + + .SEVERITY ERROR + .BASE 800 + +BADLABEL