a2cloud/setup/acmd.txt

287 lines
16 KiB
Plaintext
Raw Normal View History

2015-10-30 12:34:48 +00:00
#! /bin/bash
# vim: set tabstop=4 shiftwidth=4 expandtab filetype=sh:
2015-10-30 11:02:05 +00:00
decToHex () {
# converts single-byte decimal value to hexadecimal equivalent
# arg: decimal value from 0-255
# out: two-digit hex value from 00-FF
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $arg1 ]] || return 11
[[ $2 ]] && return 8
[[ ( $(printf %d "$arg1" 2> /dev/null) == $arg1 ) \
&& ( $arg1 -ge 0 ) && ( $arg1 -le 255 ) ]] || return 21
2015-10-30 11:02:05 +00:00
# args are valid
printf %02X "$arg1"
}
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 "-c <filename> <imagename> [[$|0x]<type>] [[$|0x]<auxtype>] synonym for -p"
echoerr " with filename and imagename reversed."
echoerr "-ad <imagename> <filename>"
echoerr " copy AppleDouble file into ProDOS disk image"
#echoerr "-cd <imagename> <filename> <dateTimeString>|<dateTimeStamp>"
#echoerr " set creation date and time of file in ProDOS disk image"
#echoerr "-md <imagename> <filename> <dateTimeString>|<dateTimeStamp>"
#echoerr " set modified date and time of file in ProDOS disk image"
2015-10-30 11:02:05 +00:00
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
}
arg1=$1
acmdStdErr="/tmp/acmd_$RANDOM$RANDOM"
[[ -f /usr/local/adtpro/adtpro.sh ]] && adtPath="/usr/local/adtpro" || adtPath=$(ls -1d /Applications/ADTPro* | head -1);
if [[ ! $2 || $arg1 == "-h" ]]; then
java -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar 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" || $arg1 == "-ad" ) && $2 && $3 ]]; then
2015-10-30 11:02:05 +00:00
adFile=
2015-10-30 11:02:05 +00:00
getArg=
if [[ $arg1 == "-p" ]]; then
prodosArg="$3"
imageArg="$2"
elif [[ $arg1 == "-ad" ]]; then
prodosArg="$3"
imageArg="$2"
[[ $prodosArg == *"/"* ]] && adFile="${prodosArg%/*}/"
adFile+=".AppleDouble/${prodosArg##*/}"
2015-10-30 11:02:05 +00:00
elif [[ $arg1 == "-c" ]]; then
prodosArg="$2"
imageArg="$3"
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##*/}"
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar $getArg "$imageArg" "$fileArg" $outFile 2> $acmdStdErr
else # put file
2015-10-30 12:38:31 +00:00
2015-10-30 11:02:05 +00:00
# test ProDOS name legitimacy
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"
2015-10-30 12:38:31 +00:00
# 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;
2015-10-30 12:38:31 +00:00
# process filetype
if [[ ! $adFile ]]; then
[[ ${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=$(decToHex $ftArg | tr [:upper:] [:lower:])
fi
ftVar="P_$fc";
[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc";
2015-10-30 11:02:05 +00:00
else
ft="$ftArg"
2015-10-30 11:02:05 +00:00
fi
# set auxtype to $0801 for Applesoft programs if not specified
[[ $ft == "BAS" && ! $auxType ]] && auxType="\$0801"
2015-10-30 12:38:31 +00:00
# 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; }
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -d "$imageArg" $prodosPath &> /dev/null
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -p "$imageArg" $prodosPath $ft $auxType < $prodosArg 2> $acmdStdErr
else
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -d "$imageArg" $prodosPath &> /dev/null
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -p "$imageArg" $prodosPath $ft $auxType 2> $acmdStdErr
fi
else # AppleDouble, get resource fork and file metadata from header file
2015-10-30 11:02:05 +00:00
[[ ! -f $prodosArg ]] && { echoerr "$prodosArg not found."; exit 1; }
[[ ! -f $adFile ]] && { echoerr "Not an AppleDouble file: $adFile"; exit 1; }
# get metadata from appleDouble header
[[ $(dd if="$adFile" bs=1 count=1 skip=741 2> /dev/null | wc -c) -gt 0 ]] && isExtFile=1 || isExtFile=
fileData=$(dd if="$adFile" bs=1 count=24 skip=637 2> /dev/null | xxd -p | tr -d '\n')
ftVar="P_${fileData:34:2}";
[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc"; # set file type
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
# convert unix timestamp to ProDOS bitfield
# yyyyyyymmmmddddd 000hhhhh00mmmmmm
cDateFields=($(date -d @$cDateTime +"%y %m %d %H %M"))
mDateFields=($(date -d @$mDateTime +"%y %m %d %H %M"))
cDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(bc <<< "obase=2;${cDateFields[0]}"))$(printf %04d $(bc <<< "obase=2;${cDateFields[1]}"))$(printf %05d $(bc <<< "obase=2;${cDateFields[2]}"))$(printf %08d $(bc <<< "obase=2;${cDateFields[3]}"))$(printf %08d $(bc <<< "obase=2;${cDateFields[4]}")) )))
cDateTimeHex=${cDateTimeHex:2:2}${cDateTimeHex:0:2}${cDateTimeHex:6:2}${cDateTimeHex:4:2}
mDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(bc <<< "obase=2;${mDateFields[0]}"))$(printf %04d $(bc <<< "obase=2;${mDateFields[1]}"))$(printf %05d $(bc <<< "obase=2;${mDateFields[2]}"))$(printf %08d $(bc <<< "obase=2;${mDateFields[3]}"))$(printf %08d $(bc <<< "obase=2;${mDateFields[4]}")) )))
mDateTimeHex=${mDateTimeHex:2:2}${mDateTimeHex:0:2}${mDateTimeHex:6:2}${mDateTimeHex:4:2}
# create forks and file entry
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
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -d "$imageArg" "${adFile%*.AppleDouble/*}$dfName" 2> /dev/null
dd if="$prodosArg" 2> /dev/null | java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -p "$imageArg" "${adFile%*.AppleDouble/*}$dfName" $00 2> $acmdStdErr
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -d "$imageArg" "${adFile%*.AppleDouble/*}$rfName" 2> /dev/null
dd if="$adFile" bs=741 skip=1 2> /dev/null | java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -p "$imageArg" "${adFile%*.AppleDouble/*}$rfName" $00 2> $acmdStdErr
fi
# create file entry, then find it
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -d "$imageArg" "${adFile%*.AppleDouble/*}$fileName" 2> /dev/null
[[ $isExtFile ]] && ddsrc="if=/dev/zero bs=512 count=1" || ddsrc="if=$prodosArg"
dd $ddsrc 2> /dev/null | java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar -p "$imageArg" "${adFile%*.AppleDouble/*}$fileName" "$ft" "$auxType" 2> $acmdStdErr
fileEntryOffset=$(grep --byte-offset --only-matching --text ".$fileName" "$imageArg" | cut -d ':' -f 1)
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=$(grep --byte-offset --only-matching --text ".$dfName" "$imageArg" | cut -d ':' -f 1)
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=$(grep --byte-offset --only-matching --text ".$rfName" "$imageArg" | cut -d ':' -f 1)
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
if [[ "${prodosPath##*/}" != "${prodosArg##*/}" ]]; then # mixed case
caseMaskDec=32768
mixedName="${prodosArg##*/}"
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
2015-10-30 11:02:05 +00:00
fi
fi
2015-10-30 12:38:31 +00:00
2015-10-30 11:02:05 +00:00
else
imageArg="$2"
java -Xmx128m -jar "$adtPath"/lib/AppleCommander/AppleCommander-ac.jar "$@" 2> $acmdStdErr
fi
helpExit