mirror of
https://github.com/RasppleII/a2cloud.git
synced 2025-01-02 21:29:37 +00:00
55c53b9c10
As noted, Ivan has agreed to allow these scripts to be relicensed under CC0. We have one file under LGPL (a unit file we lifted wholesake from systemd) and the ADTPro wrapper which I'm pretty sure Ivan wrote, but if he didn't we need to fix its license to be the same as ADTPro. Either way, to the best of my knowledge, this resolves the question of how things are licensed explicitly. (Closes #21)
422 lines
18 KiB
Bash
Executable File
422 lines
18 KiB
Bash
Executable File
#! /bin/bash
|
|
# vim: set tabstop=4 shiftwidth=4 noexpandtab filetype=sh:
|
|
|
|
# acmd - a2cloud command-line wrapper for Apple II file utilities
|
|
#
|
|
# To the extent possible under law, T. Joseph Carter and Ivan Drucker have
|
|
# waived all copyright and related or neighboring rights to the a2cloud
|
|
# scripts themselves. Software used or installed by these scripts is subject
|
|
# to other licenses. This work is published from the United States.
|
|
|
|
# set default AppleCommander location if ADTPro is not installed
|
|
defaultAcPath=$(echo -n /usr/local/bin/AppleCommander-*-ac.jar)
|
|
|
|
echoerr() {
|
|
echo "$@" 1>&2;
|
|
}
|
|
|
|
helpExit () {
|
|
if [[ -s $acmdStdErr ]]; then
|
|
if [[ $(grep CommandLineHelp $acmdStdErr) ]]; then
|
|
grep -v ^-[pge][[:space:]] $acmdStdErr | grep -v '^ or' | grep -v 0x2000 1>&2
|
|
echoerr "-g <imagename> <filename> [<outputFilename>] copy filename out of any"
|
|
echoerr " disk image. Using - for outputFilename will copy to stdout."
|
|
echoerr "-e <imagename> <filename> [<outputFilename>] like -g, with conversion"
|
|
echoerr " to modern file format if possible."
|
|
echoerr "-p <imagename> <filename> [[$|0x]<type>] [[$|0x]<auxtype>] copy filename"
|
|
echoerr " into ProDOS disk image. <type> is either three-letter or numeric"
|
|
echoerr " ProDOS file type (BIN if omitted). Will read from stdin if supplied."
|
|
echoerr " ProDOS subdirectories in <filename> will be created if needed."
|
|
echoerr " If an AppleDouble file or Nulib2 extended filename is detected,"
|
|
echoerr " <type> and <auxtype> are automatically set, and resource forks are kept."
|
|
if hash cppo &> /dev/null; then
|
|
echoerr " (To extract these from a ProDOS image file, use cppo -ad or cppo -e.)"
|
|
fi
|
|
echoerr "-c <filename> <imagename> [[$|0x]<type>] [[$|0x]<auxtype>] synonym for -p"
|
|
echoerr " with filename and imagename reversed."
|
|
else
|
|
cat $acmdStdErr
|
|
fi
|
|
if [[ $arg1 == "-h" ]]; then
|
|
exitVal=0
|
|
else
|
|
exitVal=1
|
|
fi
|
|
else
|
|
if [[ $vsd1_md5 && ( "$vsd1_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual.po)" || "$vsd2_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual2.po)" ) ]]; then
|
|
if [[ "$vsd1_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual.po)" || "$vsd2_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual2.po)" ]]; then
|
|
echoerr "One of the virtual drive image files has changed while ADTPro server is active."
|
|
echoerr " If using VSDRIVE, type 'vsdsync' now to see changes and prevent corruption."
|
|
fi
|
|
fi
|
|
exitval=0
|
|
fi
|
|
rm $acmdStdErr &> /dev/null
|
|
exit $exitVal
|
|
}
|
|
|
|
decToBin () {
|
|
dec="$(( 10#$1 ))"
|
|
bits=""
|
|
for i in 7 6 5 4 3 2 1 0; do
|
|
bits+=$(( (dec & (2**i)) >> i ))
|
|
done
|
|
echo -n "$(( 10#$bits ))"
|
|
}
|
|
|
|
arg1=$1
|
|
|
|
acmdStdErr="/tmp/acmd_$RANDOM$RANDOM"
|
|
|
|
[[ -f /usr/libexec/java_home ]] && osx=1 || osx=
|
|
|
|
if [[ $osx ]]; then
|
|
if ! /usr/libexec/java_home &> /dev/null; then
|
|
echo -n "AppleCommander requires the Java JDK. Do you want to install it now? "
|
|
read
|
|
if [[ ${REPLY:0:1} == "Y" || ${REPLY:0:1} == "y" ]]; then
|
|
# install Oracle Java for OS X
|
|
echo "Downloading Java JDK..."
|
|
mkdir -p /tmp/jdk
|
|
curl -L --cookie oraclelicense=accept-securebackup-cookie $(curl -L "http://www.oracle.com/"$(curl -L "http://www.oracle.com/technetwork/java/javase/downloads/" 2> /dev/null | grep -o -m 1 '/technetwork/java/javase/downloads/jdk.-downloads-[0-9]*\.html') 2> /dev/null | grep -o 'http://.*macosx-x64.dmg' | sort | tail -1) 2> /dev/null > /tmp/jdk/jdk.dmg
|
|
echo "Installing Java JDK..."
|
|
hdiutil attach /tmp/jdk/jdk.dmg &> /dev/null
|
|
sudo installer -pkg /Volumes/JDK*/JDK*.pkg -target /
|
|
diskutil unmountDisk /Volumes/JDK*
|
|
rm -r /tmp/jdk 2> /dev/null
|
|
if ! /usr/libexec/java_home &> /dev/null; then
|
|
echo "The Java JDK could not be installed."
|
|
echo "Type \"java\" and then click \"More Info...\" to download and install it manually."
|
|
exit 1
|
|
fi
|
|
else
|
|
exit 1
|
|
fi
|
|
fi
|
|
else # not OS X, so presumably Linux
|
|
if ! hash java &> /dev/null; then
|
|
echo -n "AppleCommander requires the Java JDK. "
|
|
if hash apt-get; then
|
|
echo -n "Do you want to install it now? "
|
|
read
|
|
if [[ ${REPLY:0:1} == "Y" || ${REPLY:0:1} == "y" ]]; then
|
|
# from http://www.webupd8.org/2012/06/how-to-install-oracle-java-7-in-debian.html
|
|
if { ! grep -q webupd8team /etc/apt/sources.list; }; then
|
|
{
|
|
echo;
|
|
echo "# Oracle Java JDK";
|
|
echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main";
|
|
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main";
|
|
} | sudo tee -a /etc/apt/sources.list > /dev/null
|
|
fi
|
|
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
|
|
sudo apt-get -y update
|
|
echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
|
|
sudo apt-get -y install oracle-java8-installer
|
|
sudo apt-get -y clean
|
|
else
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
if ! hash java &> /dev/null; then
|
|
echo
|
|
echo "Get it from http://www.oracle.com/technetwork/java/javase/downloads/"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
setAcPath () {
|
|
acPath=$defaultAcPath
|
|
adtPath=
|
|
if [[ ! -f $acPath ]]; then
|
|
if [[ -f /usr/local/adtpro/adtpro.sh ]]; then
|
|
adtPath="/usr/local/adtpro"
|
|
elif [[ $osx ]]; then
|
|
adtPath=$(echo -n /Applications/ADTPro* | tr ' ' '\n' | sort | head -1);
|
|
fi
|
|
if [[ -d "$adtPath" ]]; then
|
|
acPath="$adtPath"/lib/AppleCommander/AppleCommander-ac.jar
|
|
[[ ! -f $acPath ]] && acPath=$(echo -n "$adtPath"/lib/AppleCommander/AppleCommander-*-ac.jar)
|
|
fi
|
|
fi
|
|
acPath=$(echo -n $acPath)
|
|
}
|
|
|
|
setAcPath
|
|
if [[ ! -f $acPath ]]; then
|
|
echo "Installing AppleCommander..."
|
|
sudo mkdir -p /usr/local/bin
|
|
acUrl="http://sourceforge.net/projects/applecommander/files/AppleCommander%20-%20Interim/testcase/AppleCommander-1.3.5.13id-ac.jar/download"
|
|
if [[ $osx ]]; then
|
|
curl -L "$acUrl" 2> /dev/null | sudo tee /usr/local/bin/AppleCommander-1.3.5.13id-ac.jar > /dev/null
|
|
else
|
|
sudo wget -qO /usr/local/bin/AppleCommander-1.3.5.13id-ac.jar "$acUrl"
|
|
fi
|
|
setAcPath
|
|
if [[ ! -f "$acPath" ]]; then
|
|
echo "AppleCommander couldn't be installed. Download it from"
|
|
echo "http://applecommander.sourceforge.net and put it in /usr/local/bin."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
ac="java -Xmx128m -jar $acPath"
|
|
|
|
if [[ ! $2 || $arg1 == "-h" ]]; then
|
|
java -jar "$acPath" 2> $acmdStdErr
|
|
[[ $? -eq 127 ]] && exit 127 || helpExit $arg1
|
|
fi
|
|
|
|
if [[ $arg1 != "-i" && $arg1 != "-ls" && $arg1 != "-l" && $arg1 != "-ll" && $arg1 != "-x" && $arg1 != "-g" && $arg1 != "-e" && $(ps aux | grep [A]DTPro) ]]; then
|
|
vsd1_md5="$(md5sum /usr/local/adtpro/disks/Virtual.po)"
|
|
vsd2_md5="$(md5sum /usr/local/adtpro/disks/Virtual2.po)"
|
|
fi
|
|
|
|
if [[ ( $arg1 == "-p" || $arg1 == "-c" || $arg1 == "-g" || $arg1 == "-e" ) && $2 && $3 ]]; then
|
|
|
|
AD=
|
|
EX=
|
|
prodosArg=
|
|
prodosArgParent=
|
|
rFile=
|
|
getArg=
|
|
if [[ $arg1 == "-c" || $arg1 == "-p" ]]; then
|
|
if [[ $arg1 == "-c" ]]; then
|
|
prodosArg="$2"
|
|
imageArg="$3"
|
|
elif [[ $arg1 == "-p" ]]; then
|
|
prodosArg="$3"
|
|
imageArg="$2"
|
|
fi
|
|
[[ $prodosArg == *"/"* ]] && prodosArgParent="${prodosArg%/*}/"
|
|
rFile="${prodosArgParent}.AppleDouble/${prodosArg##*/}"
|
|
if [[ -f "$prodosArg" && -f "$rFile" ]]; then # AppleDouble
|
|
AD=1
|
|
elif [ -f "${prodosArg%#*}"#?????? ]; then # Nulib2 ext filename
|
|
EX=1
|
|
prodosArg=$(echo -n "${prodosArg%#*}"#??????)
|
|
rFile="${prodosArg}"r
|
|
fi
|
|
if [[ $AD || $EX ]]; then
|
|
# if target is not a ProDOS disk, ignore metadata
|
|
if ! $ac -i "$imageArg" | grep -q '^Disk Format: ProDOS'; then
|
|
AD=
|
|
EX=
|
|
fi
|
|
fi
|
|
elif [[ $arg1 == "-g" || $arg1 == "-e" ]]; then
|
|
fileArg="$3"
|
|
imageArg="$2"
|
|
getArg="$arg1"
|
|
else
|
|
exit 2;
|
|
fi
|
|
|
|
shift
|
|
|
|
if [[ $getArg ]]; then # get file
|
|
|
|
outFile=
|
|
[[ $3 && $3 != "-" ]] && outFile="$3"
|
|
[[ ! $3 ]] && outFile="${2##*/}"
|
|
$ac $getArg "$imageArg" "$fileArg" $outFile 2> $acmdStdErr
|
|
|
|
else # put file
|
|
|
|
# test ProDOS name validity
|
|
prodosPath=$(tr [:lower:] [:upper:] <<< "${prodosArg%#*}" )
|
|
IFS_orig="$IFS"; IFS="/";
|
|
prodosPathParts="$prodosPath"
|
|
for thisProdosPathPart in $prodosPathParts; do
|
|
if [[ ${#thisProdosPathPart} -gt 15 || ! $(grep ^[A-Z][0-9A-Z\.]*$ <<< $thisProdosPathPart) ]]; then
|
|
echoerr "Invalid ProDOS name: $prodosPath"; exit 1;
|
|
fi
|
|
done
|
|
IFS="$IFS_orig"
|
|
|
|
# filetype to name table
|
|
P_00=UNK; P_01=BAD; P_02=PCD; P_03=PTX; P_04=TXT; P_05=PDA; P_06=BIN; P_07=FNT; P_08=FOT; P_09=BA3; P_0a=DA3; P_0b=WPF; P_0c=SOS; P_0f=DIR; P_10=RPD; P_11=RPI; P_12=AFD; P_13=AFM; P_14=AFR; P_15=SCL; P_16=PFS; P_19=ADB; P_1a=AWP; P_1b=ASP; P_20=TDM; P_21=IPS; P_22=UPV; P_29=3SD; P_2a=8SC; P_2b=8OB; P_2c=8IC; P_2d=8LD; P_2e=P8C; P_41=OCR; P_42=FTD; P_50=GWP; P_51=GSS; P_52=GDB; P_53=DRW; P_54=GDP; P_55=HMD; P_56=EDU; P_57=STN; P_58=HLP; P_59=COM; P_5a=CFG; P_5b=ANM; P_5c=MUM; P_5d=ENT; P_5e=DVU; P_60=PRE; P_6b=BIO; P_6d=DVR; P_6e=PRE; P_6f=HDV; P_80=GEZ; P_81=GE1; P_82=GEO; P_83=GE3; P_84=GE4; P_85=GE5; P_86=GE6; P_87=GE7; P_88=GE8; P_89=GE9; P_8a=GEA; P_8b=GEB; P_8c=GEC; P_8d=GED; P_8e=GEE; P_8f=GEF; P_a0=WP_; P_ab=GSB; P_ac=TDF; P_ad=BDF; P_b0=SRC; P_b1=OBJ; P_b2=LIB; P_b3=S16; P_b4=RTL; P_b5=EXE; P_b6=STR; P_b7=TSF; P_b8=NDA; P_b9=CDA; P_ba=TOL; P_bb=DRV; P_bc=LDF; P_bd=FST; P_bf=DOC; P_c0=PNT; P_c1=PIC; P_c2=ANI; P_c3=PAL; P_c5=OOG; P_c6=SCR; P_c7=CDV; P_c8=FON; P_c9=FND; P_ca=ICN; P_d5=MUS; P_d6=INS; P_d7=MDI; P_d8=SND; P_db=DBM; P_e0=SHK; P_e2=DTS; P_ee=R16; P_ef=PAS; P_f0=CMD; P_f9=P16; P_fa=INT; P_fb=IVR; P_fc=BAS; P_fd=VAR; P_fe=REL; P_ff=SYS;
|
|
|
|
# process filetype
|
|
if [[ ! $AD && ! $EX ]]; then # no resource fork or metadata
|
|
[[ ${3:0:2} == "0x" ]] && ftArg="\$${3:2}" || ftArg="$3"
|
|
auxType="$4"
|
|
|
|
# assume BIN/$2000 if filetype omitted
|
|
if [[ ! $ftArg ]]; then
|
|
ft="BIN"
|
|
auxType="\$2000"
|
|
# accept hex or decimal number for file type
|
|
elif [[ ( ${ftArg:0:1} == '$' && ${#ftArg} -eq 3 ) || $(grep [0-9] <<< ${ftArg:0:1}) ]]; then
|
|
if [[ ${ftArg:0:1} == '$' ]]; then
|
|
fc=$(tr [:upper:] [:lower:] <<< ${ftArg:1:2})
|
|
else
|
|
fc=$(printf %02X "$ftArg" | tr [:upper:] [:lower:])
|
|
fi
|
|
ftVar="P_$fc";
|
|
[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc";
|
|
else
|
|
ft="$ftArg"
|
|
fi
|
|
|
|
# set auxtype to $0801 for Applesoft programs if not specified
|
|
[[ $ft == "BAS" && ! $auxType ]] && auxType="\$0801"
|
|
|
|
# test for absence of stdin [[ -t 0 ]] and if absent use ProDOS name
|
|
if [[ -t 0 ]]; then
|
|
[[ ! -f $prodosArg ]] && { echoerr "$prodosArg not found."; exit 1; }
|
|
$ac -d "$imageArg" $prodosPath &> /dev/null
|
|
$ac -p "$imageArg" $prodosPath $ft $auxType < $prodosArg 2> $acmdStdErr
|
|
else
|
|
$ac -d "$imageArg" $prodosPath &> /dev/null
|
|
$ac -p "$imageArg" $prodosPath $ft $auxType 2> $acmdStdErr
|
|
fi
|
|
else # AppleDouble or nulib extended, get resource fork and file metadata from header file
|
|
|
|
[[ ! -f $prodosArg ]] && { echoerr "$prodosArg not found."; exit 1; }
|
|
[[ $AD && ! -f $rFile ]] && { echoerr "Not an AppleDouble file: $rFile"; exit 1; }
|
|
|
|
if [[ $AD ]]; then
|
|
# get metadata from AppleDouble header
|
|
[[ $(dd if="$rFile" bs=1 count=1 skip=741 2> /dev/null | wc -c) -gt 0 ]] && isExtFile=1 || isExtFile=
|
|
fileData=$(dd if="$rFile" bs=1 count=24 skip=637 2> /dev/null | xxd -p | tr -d '\n')
|
|
ftVar="P_${fileData:34:2}";
|
|
auxType="\$"${fileData:36:4}
|
|
cDateTime=$(printf %d 0x${fileData:0:8})
|
|
mDateTime=$(printf %d 0x${fileData:8:8})
|
|
[[ $(printf %d 0x"${fileData:0:2}") -gt 127 ]] && (( cDateTime-=4294967296 )) # handle negative hex number
|
|
[[ $(printf %d 0x"${fileData:8:2}") -gt 127 ]] && (( mDateTime-=4294967296 )) # handle negative hex number
|
|
(( cDateTime+=946684800 )) # convert AD timestamp to Unix timestamp
|
|
(( mDateTime+=946684800 )) # convert AD timestamp to Unix timestamp
|
|
else # EX
|
|
# get metadata from file info
|
|
[[ -f "$rFile" ]] && isExtFile=1 || isExtFile=
|
|
ftVar="P_${prodosArg: -6:2}"
|
|
auxType="\$"${prodosArg: -4}
|
|
if [[ $osx ]]; then
|
|
mDateTime=$(stat -f "%m" "$prodosArg")
|
|
else
|
|
mDateTime=$(stat -c %Y "$prodosArg")
|
|
fi
|
|
cDateTime=$mDateTime
|
|
fi
|
|
[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc"; # set file type
|
|
# convert unix timestamp to ProDOS bitfield yyyyyyymmmmddddd 000hhhhh00mmmmmm
|
|
cDateFields=($(date -d @$cDateTime +"%y %m %d %H %M" 2> /dev/null))
|
|
[[ ! $cDateFields ]] && cDateFields=($(date -r $cDateTime +"%y %m %d %H %M")) # OS X/BSD
|
|
mDateFields=($(date -d @$mDateTime +"%y %m %d %H %M" 2> /dev/null))
|
|
[[ ! $mDateFields ]] && mDateFields=($(date -r $mDateTime +"%y %m %d %H %M")) # OS X/BSD
|
|
cDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(decToBin ${cDateFields[0]}))$(printf %04d $(decToBin ${cDateFields[1]}))$(printf %05d $(decToBin ${cDateFields[2]}))$(printf %08d $(decToBin ${cDateFields[3]}))$(printf %08d $(decToBin ${cDateFields[4]})) )))
|
|
cDateTimeHex=${cDateTimeHex:2:2}${cDateTimeHex:0:2}${cDateTimeHex:6:2}${cDateTimeHex:4:2}
|
|
mDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(decToBin ${mDateFields[0]}))$(printf %04d $(decToBin ${mDateFields[1]}))$(printf %05d $(decToBin ${mDateFields[2]}))$(printf %08d $(decToBin ${mDateFields[3]}))$(printf %08d $(decToBin ${mDateFields[4]})) )))
|
|
mDateTimeHex=${mDateTimeHex:2:2}${mDateTimeHex:0:2}${mDateTimeHex:6:2}${mDateTimeHex:4:2}
|
|
|
|
# create forks and file entry
|
|
### RIGHT HERE PROBLEMS IF #000000 specified and maybe if not
|
|
fileName="${prodosPath##*/}"
|
|
if [[ $isExtFile ]]; then
|
|
dfName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
|
|
while [[ ! $rfName || $rfName == $dfName ]]; do
|
|
rfName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
|
|
done
|
|
while [[ $fileName == "${prodosPath##*/}" || $rfName == $fileName || $dfName == $fileName ]]; do
|
|
fileName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
|
|
done
|
|
$ac -d "$imageArg" "${prodosArgParent}$dfName" 2> /dev/null
|
|
dd if="$prodosArg" 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$dfName" $00 2> $acmdStdErr
|
|
$ac -d "$imageArg" "${prodosArgParent}$rfName" 2> /dev/null
|
|
dd if="$rFile" bs=1 skip=$(( 0$AD ? 741 : 0 )) 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$rfName" $00 2> $acmdStdErr
|
|
fi
|
|
|
|
# create file entry, then find it
|
|
$ac -d "$imageArg" "${prodosArgParent}$fileName" 2> /dev/null
|
|
[[ $isExtFile ]] && ddsrc="if=/dev/zero bs=512 count=1" || ddsrc="if=$prodosArg"
|
|
dd $ddsrc 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$fileName" "$ft" "$auxType" 2> $acmdStdErr
|
|
# thx to http://unix.stackexchange.com/a/122945/99697 for perl alternative to broken --byte-offset in grep 2.5.1 (e.g. in OS X)
|
|
fileEntryOffset=$(perl -n0777e "print pos()-length('.$fileName') while /.$fileName/g" < "$imageArg")
|
|
fileEntry=$(dd if="$imageArg" bs=1 count=39 skip=$fileEntryOffset 2> /dev/null | xxd -p | tr -d '\n')
|
|
|
|
if [[ $isExtFile ]]; then
|
|
extKeyBlockOffset=$(( ( ( $(printf %d 0x"${fileEntry:36:2}") * 256 ) + $(printf %d 0x"${fileEntry:34:2}") * 512 ) ))
|
|
|
|
# find data fork, copy storage type, key block, block size, length to extended key block mini-entry
|
|
# then mark as available/deleted
|
|
dfOffset=$(perl -n0777e "print pos()-length('.$dfName') while /.$dfName/g" < "$imageArg")
|
|
dfEntry=$(dd if="$imageArg" bs=1 count=39 skip=$dfOffset 2> /dev/null | xxd -p | tr -d '\n')
|
|
dfStorageType=$(printf %02X $(( $(printf %d 0x${dfEntry:0:2}) >> 4 )) )
|
|
dfBlocksUsed=$(( ( $(printf %d 0x${dfEntry:40:2}) * 256 ) + $(printf %d 0x${dfEntry:38:2}) ))
|
|
dfInfo=${dfEntry:34:14}
|
|
echo -n -e \\x"$dfStorageType"$(sed 's/../\\x&/g' <<< $dfInfo) \
|
|
| dd of="$imageArg" conv=notrunc bs=1 seek=$(( extKeyBlockOffset+0 )) 2> /dev/null
|
|
# mark as deleted
|
|
echo -n -e \\x0${dfEntry:1:1} \
|
|
| dd of="$imageArg" conv=notrunc bs=1 seek=$(( dfOffset+0 )) 2> /dev/null
|
|
|
|
# find resource fork, copy storage type, key block, block size, length to extended key block mini-entry
|
|
# then mark as available/deleted
|
|
rfOffset=$(perl -n0777e "print pos()-length('.$rfName') while /.$rfName/g" < "$imageArg")
|
|
rfEntry=$(dd if="$imageArg" bs=1 count=39 skip=$rfOffset 2> /dev/null | xxd -p | tr -d '\n')
|
|
rfStorageType=$(printf %02X $(( $(printf %d 0x${rfEntry:0:2}) >> 4 )) )
|
|
rfBlocksUsed=$(( ( $(printf %d 0x${rfEntry:40:2}) * 256 ) + $(printf %d 0x${rfEntry:38:2}) ))
|
|
rfInfo=${rfEntry:34:14}
|
|
echo -n -e \\x"$rfStorageType"$(sed 's/../\\x&/g' <<< $rfInfo) \
|
|
| dd of="$imageArg" conv=notrunc bs=1 seek=$(( extKeyBlockOffset+256 )) 2> /dev/null
|
|
# mark as deleted
|
|
echo -n -e \\x0${rfEntry:1:1} \
|
|
| dd of="$imageArg" conv=notrunc bs=1 seek=$(( rfOffset+0 )) 2> /dev/null
|
|
|
|
# reduce active file count in directory by two
|
|
parentDirKeyBlockOffset=$(( ( ( $(printf %d 0x"${fileEntry:76:2}") * 256 ) + $(printf %d 0x"${fileEntry:74:2}") * 512 ) ))
|
|
fileCountHex=$(dd if="$imageArg" bs=1 count=2 skip=$((parentDirKeyBlockOffset+4+33)) 2> /dev/null | xxd -p)
|
|
fileCount=$(( ( $(printf %d 0x${fileCountHex:2:2}) * 256 ) + $(printf %d 0x${fileCountHex:0:2}) ))
|
|
fileCountHex=$(printf %04X $((fileCount - 2)))
|
|
echo -n -e \\x${fileCountHex:2:2}\\x${fileCountHex:0:2} \
|
|
| dd of="$imageArg" conv=notrunc bs=1 seek=$((parentDirKeyBlockOffset+4+33)) 2> /dev/null
|
|
|
|
# update extended file metadata
|
|
|
|
# storage type (5), name length, name
|
|
name="${prodosPath##*/}"
|
|
nameLen=${#name}
|
|
nameHeader=$(printf %02X $((nameLen + 80)) )
|
|
nameField=$(echo -n $name | xxd -p | tr -d '\n' | sed -e :a -e 's/^.\{1,29\}$/&00/;ta')
|
|
|
|
# blocks used
|
|
blocksUsed=$(( dfBlocksUsed + rfBlocksUsed + 1 ))
|
|
|
|
# store updated metadata
|
|
fileEntry=${nameHeader}${nameField}${fileEntry:32}
|
|
fi
|
|
|
|
# put creation and modified date in file entry
|
|
fileEntry=${fileEntry:0:48}${cDateTimeHex}${fileEntry:56:10}${mDateTimeHex}${fileEntry:74:4}
|
|
|
|
# put casemask for mixed case filename in file entry
|
|
[[ $EX ]] && prodosArg="${prodosArg%#*}"
|
|
if [[ "${prodosPath##*/}" != "${prodosArg##*/}" ]]; then # mixed case
|
|
caseMaskDec=32768
|
|
mixedName="${prodosArg##*/}"
|
|
[[ $EX ]] && mixedName="${mixedName%#*}"
|
|
for (( i=0; i<${#mixedName}; i++ )); do
|
|
[[ "${mixedName:$i:1}" == $(tr [:lower:] [:upper:] <<< "${mixedName:$i:1}") ]] # $? == 0 means uppercase
|
|
(( caseMaskDec+=$(( $? * (2**(14-i)) )) ))
|
|
done
|
|
caseMaskHex=$(printf %04X $caseMaskDec)
|
|
fileEntry=${fileEntry:0:56}${caseMaskHex:2:2}${caseMaskHex:0:2}${fileEntry:60}
|
|
fi
|
|
|
|
# write updated metadata to file entry
|
|
echo -n -e $(sed 's/../\\x&/g' <<< $fileEntry) | dd of="$imageArg" bs=1 conv=notrunc seek=$fileEntryOffset 2> /dev/null
|
|
fi
|
|
fi
|
|
|
|
else
|
|
|
|
imageArg="$2"
|
|
$ac "$@" 2> $acmdStdErr
|
|
|
|
fi
|
|
|
|
helpExit
|