a2cloud/docs/ivanx/setup/cppo-bash.txt
2015-10-28 16:27:57 -07:00

762 lines
26 KiB
Bash

#!/bin/bash
# need to test:
# does both -e and -ad work as expected? what if you do both?
#--ID-bashbyter routines
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
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ( $(printf %d "$1" 2> /dev/null) == $1 ) \
&& ( $1 -ge 0 ) && ( $1 -le 255 ) ]] || return 21
# args are valid
printf %02X "$1"
}
hexToDec () {
# converts single-byte hexadecimal value to decimal equivalent
# arg: two-digit hex value from 00-FF
# out: decimal value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
printf %d "0x$1"
}
hexToBin () {
# converts single-byte hexadecimal value to binary string
# arg: two-digit hex value from 00-FF
# out: binary string value
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
for n in 0 1; do
if [[ ${1:n:1} == "0" ]]; then b="0000"
elif [[ ${1:n:1} == "1" ]]; then b="0001"
elif [[ ${1:n:1} == "2" ]]; then b="0010"
elif [[ ${1:n:1} == "3" ]]; then b="0011"
elif [[ ${1:n:1} == "4" ]]; then b="0100"
elif [[ ${1:n:1} == "5" ]]; then b="0101"
elif [[ ${1:n:1} == "6" ]]; then b="0110"
elif [[ ${1:n:1} == "7" ]]; then b="0111"
elif [[ ${1:n:1} == "8" ]]; then b="1000"
elif [[ ${1:n:1} == "9" ]]; then b="1001"
elif [[ ${1:n:1} == "A" ]]; then b="1010"
elif [[ ${1:n:1} == "B" ]]; then b="1011"
elif [[ ${1:n:1} == "C" ]]; then b="1100"
elif [[ ${1:n:1} == "D" ]]; then b="1101"
elif [[ ${1:n:1} == "E" ]]; then b="1110"
elif [[ ${1:n:1} == "F" ]]; then b="1111"
fi
echo -n $b
done
}
binToDec () {
# converts single-byte binary string (8 bits) value to decimal
# warning: no error checking
# arg: binary string up to 8 bits
# out: decimal value
dec=0
bits=$1
while (( ${#bits} < 8 )); do
bits="0$bits"
done
for n in {0..7}; do
(( dec+=( ${bits:$n:1} * ( 2**(7-$n) ) ) ))
done
echo -n $dec
}
binToHex () {
# converts single-byte binary string (8 bits) value to hex
# warning: no error checking
# arg: binary string up to 8 bits
# out: hex value
echo $(decToHex $(binToDec $1))
}
charToDec () {
# converts single character to corresponding decimal value
# stdin OR arg: one character
# [arg overrides stdin; stdin is required for NUL (0) or LF (0x0A)]
# out: decimal value from 0-255
#exit: 8=extraneous arg, 9=invalid stdin,
# 11=missing stdin/arg, 21=invalid arg
[[ ( ! -t 0 ) && $1 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $2 ]] && return 8; [[ $1 ]] || return 11; }
# arg/stdin is potentially valid (additional check below)
charX="$1X"; [[ $1 ]] || charX="$(cat; echo -n 'X';)"
[[ ${#charX} -le 2 ]] || return $(( $([[ $1 ]]; echo $?) ? 9 : 21 ))
# above line verifies that arg/stdin is valid
[[ ${#charX} -ne 2 ]] && { echo -n 0; return 0; }
echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n'
}
charToHex () {
# converts single character to corresponding hexadecimal value
# stdin OR arg: one character
# [arg overrides stdin; stdin is required for NUL (0) or LF (0x0A)]
# out: decimal value from 0-255
#exit: 8=extraneous arg, 9=invalid stdin,
# 11=missing stdin/arg, 21=invalid arg
[[ ( ! -t 0 ) && $1 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $2 ]] && return 8; [[ $1 ]] || return 11; }
# arg/stdin is potentially valid (additional check below)
charX="$1X"; [[ $1 ]] || charX="$(cat; echo -n 'X';)"
[[ ${#charX} -le 2 ]] || return $(( $([[ $1 ]]; echo $?) ? 9 : 21 ))
# above line verifies that stdin/arg is valid
[[ ${#charX} -ne 2 ]] && { echo -n "00"; return 0; }
printf %02X $(echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n')
}
decToChar () {
# converts single-byte decimal value to equivalent character
# arg: decimal number from 0-255
# out: one character
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ( $(printf %d "$1" 2> /dev/null ) == $1 ) \
&& ( $1 -ge 0 ) && ( $1 -le 255 ) ]] || return 21
# args are valid
echo -n -e "\x$(printf %02X "$1")"
}
hexToChar () {
# converts single-byte hexadecimal value to corresponding character
# arg: two-digit hexadecimal number from 00-FF
# out: one character
#exit: 8=extraneous arg, 11=missing arg, 21=invalid arg
[[ $1 ]] || return 11
[[ $2 ]] && return 8
[[ ${#1} -eq 2 ]] || return 21
[[ $(printf %02X "0x$1" 2> /dev/null) == \
$(echo -n "$1" | tr [a-z] [A-Z]) ]] || return 21
# args are valid
echo -n -e "\x$1"
}
readchars () {
# read one or more characters from a file
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# arg3: (optional) # of chars to read (default is until end of file)
# out: sequence of characters
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11
[[ $4 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
[[ $3 ]] && { [[ ( $(printf %d "$3" 2> /dev/null) == $3 ) \
&& ( $3 -ge 0 ) ]] || return 23; }
# args are valid
dd if="$1" bs=1 skip=$(($2)) $([[ $3 ]] && echo -n "count=$3") \
2> /dev/null
}
readcharDec () {
# read one character from file & convert to equivalent decimal value
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# out: decimal value from 0-255
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2
[[ $1 ]] || return 11
[[ $3 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
# args are valid
charX="$(dd if="$1" bs=1 skip=$(($2)) \
count=1 2> /dev/null; echo -n X)"
[[ ${#charX} -gt 1 ]] || { echo -n 0; return 0; }
echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n'
}
readcharHex () {
# read one character from file & convert to corresponding hex value
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before reading)
# out: two-digit hex value from 00-FF
# exit: 8=extraneous arg, 11=missing arg1,
# 21=invalid arg1, 22=invalid arg2
[[ $1 ]] || return 11
[[ $3 ]] && return 8
[[ -f $1 ]] || return 21
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22; }
# args are valid
charX="$(dd if="$1" bs=1 skip=$(($2)) \
count=1 2> /dev/null; echo -n X)"
[[ ${#charX} -gt 1 ]] || { echo -n "00"; return 0; }
printf %02X $(echo -n "${charX:0:1}" | od -t u1 | \
head -1 | sed 's/[0\ ]*//' | tr -d ' \n')
}
### 2-15-11 above tested on OS X and Linux
writechars () {
# write one or more characters (bytes) to file
# arg1: filename
# arg2: (optional) offset (# of bytes to skip before writing)
# arg3 OR stdin: sequence of characters
# [stdin required if writing NUL (0) or trailing LF (0x0A) chars]
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1,
# 13=missing stdin/arg3, 22=invalid arg2
[[ $1 ]] || { [[ -t 0 ]] || cat > /dev/null; return 11; }
[[ $2 ]] && { [[ ( $(printf %d "$2" 2> /dev/null) == $2 ) && \
( $2 -ge 0 ) ]] || { [[ -t 0 ]] || cat > /dev/null; return 22; } }
[[ ( ! -t 0 ) && $3 ]] && { cat > /dev/null; return 8; }
[[ ( -t 0 ) ]] && { [[ $4 ]] && return 8; [[ $3 ]] || return 13; }
# args are valid
if [[ -t 0 ]]; then
echo -n "$3" | \
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
else
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
fi
}
writecharDec () {
# write corresponding character of single-byte decimal value into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: decimal number from 0-255
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
# out: nothing
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
[[ ( $(printf %d "$3" 2> /dev/null) == $3 ) \
&& ( $3 -ge 0 ) && ( $3 -lt 255 ) ]] || return 23
# args are valid
echo -n -e "\x$(printf %02X "$3")" | \
dd of="$1" bs=1 seek=$(($2)) conv=notrunc 2> /dev/null
}
writecharHex () {
# write corresponding character of single-byte hex value into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: two-digit hexadecimal number from 00-FF
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
[[ $(printf %02X "0x$3" 2> /dev/null) == \
$(echo -n "$3" | tr [a-z] [A-Z]) ]] || return 23
# args are valid
echo -n -e "\x$3" | \
dd of="$1" bs=1 seek=$2 conv=notrunc 2> /dev/null
}
writecharsHex () {
# write corresponding characters of hex values into file
# arg1: filename
# arg2: offset (# of bytes to skip before writing)
# arg3: string of two-digit hexadecimal numbers from 00-FF, period delimited (not checked!)
# out: nothing
# exit: 8=extraneous arg, 11=missing arg1, 12=missing arg2,
# 13=missing arg3, 22=invalid arg2, 23=invalid arg3
[[ $1 ]] || return 11; [[ $2 ]] || return 12; [[ $3 ]] || return 13
[[ $4 ]] && return 8
[[ ( $(printf %d "$2" 2> /dev/null) == $2 ) \
&& ( $2 -ge 0 ) ]] || return 22
p=0
offset=$2
len=${#3}
while (( p < len )); do
outByte=${3:$p:2}
[[ $(printf %02X "0x$outByte" 2> /dev/null) == \
$(echo -n "$outByte" | tr [a-z] [A-Z]) ]] || return 23
# args are valid
echo -n -e "\x$outByte" | \
dd of="$1" bs=1 seek=$offset conv=notrunc 2> /dev/null
(( p += 3 ))
(( offset++ ))
done
}
# ---
pdosDateToUnixDate () {
# input: ProDOS date/time bit sequence string in format:
# yyyyyyymmmmddddd000hhhhh00mmmmmm
# output: seconds since Unix epoch (1-Jan-1970), or current date/time if no ProDOS date
year=$(( $(binToDec ${1:0:7}) + 1900 ))
(( $year < 1940 )) && (( year+=100 ))
month=$(binToDec ${1:7:4})
day=$(binToDec ${1:11:5})
hour=$(binToDec ${1:19:5})
minute=$(binToDec ${1:26:6})
date -d "$year-$month-$day $hour:$minute:00" "+%s" 2> /dev/null
}
unixDateToADDate () {
# input: seconds since Unix epoch (1-Jan-1970 00:00:00 GMT)
# output: seconds since Netatalk epoch (1-Jan-2000 00:00:00 GMT),
# in four period-delimited hex bytes (big endian)
adDate=$(( $1 - 946684800 ));
if (( $adDate < 0 )); then
(( adDate+=4294967296 )) # to get negative hex number
fi
adDateHex=$(printf %08X $adDate)
echo "${adDateHex:0:2}.${adDateHex:2:2}.${adDateHex:4:2}.${adDateHex:6:2}"
}
# cppo support routines:
# arg1: directory block
# arg2: file index (if applicable)
# arg3: directory chunk # (if applicable)
# returns byte position in disk image file
getStartPos () {
echo $(( ($1 * 512) + (39 * ( ($2 + ($2>11) ) % 13) ) + ( ($2>11) ? 4 : 43) ))
}
getStorageType () {
start=$(getStartPos $1 $2)
firstByte=$(readcharDec "$image" $start)
echo $(($firstByte/16))
}
getFileName () {
start=$(getStartPos $1 $2)
firstByte=$(readcharDec "$image" $start)
entryType=$(($firstByte/16))
nameLength=$(($firstByte-$entryType*16))
echo $(readchars "$image" $(($start+1)) $nameLength)
}
getFileType () {
start=$(getStartPos $1 $2)
echo $(readcharHex "$image" $(($start+16)) )
}
getKeyPointer () {
start=$(getStartPos $1 $2)
echo $(( $(readcharDec "$image" $(($start+17)) ) + \
$(readcharDec "$image" $(($start+18)) ) * 256 ))
}
getFileLength () {
start=$(getStartPos $1 $2)
echo $(( $(readcharDec "$image" $(($start+21)) ) + \
$(readcharDec "$image" $(($start + 22)) ) * 256 + \
$(readcharDec "$image" $(($start + 23)) ) * 65536 ))
}
getAuxType () {
start=$(getStartPos $1 $2)
echo $(readcharHex "$image" $(($start+32)) ).$(readcharHex "$image" $(($start+31)) )
}
getCreationDate () {
#outputs prodos creation date/time as Unix time (seconds since Jan 1 1970 GMT)
#or "NONE" if there is none
start=$(getStartPos $1 $2)
pdosDate=\
$( hexToBin $(readcharHex "$image" $(($start+25)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+24)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+27)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+26)) ) )
pdosDateToUnixDate $pdosDate || echo "NONE"
}
getModifiedDate () {
#outputs prodos modified date/time as Unix time (seconds since Jan 1 1970 GMT)
start=$(getStartPos $1 $2)
pdosDate=\
$( hexToBin $(readcharHex "$image" $(($start+34)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+33)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+36)) ) )\
$( hexToBin $(readcharHex "$image" $(($start+35)) ) )
pdosDateToUnixDate $pdosDate || echo "NONE"
}
#isLocked () {
# #returns 1 (meaning locked) if bit 7, 6, or 1 are clear; otherwise returns 0
# start=$(getStartPos $1 $2)
# access=$( $hexToBin $(readcharHex "$image" $(($start+30)) ) )
# if [[ ${access:0:1} != "1" || ${access:1:1} != "1" || ${access:7:1} != "1" ]]; then
# echo 1
# else
# echo 0
# fi
#}
getVolumeName () {
echo $(getWorkingDirName 2)
}
getWorkingDirName () {
start=$(( $1 * 512 ))
firstByte=$(readcharDec "$image" $(($start+4)) )
entryType=$(($firstByte/16))
nameLength=$(($firstByte-$entryType*16))
echo $(readchars "$image" $(($start+5)) $nameLength)
}
getDirEntryCount () {
start=$(( $1 * 512 ))
echo $(( $(readcharDec "$image" $(($start+37)) ) + \
$(readcharDec "$image" $(($start+38)) ) * 256 ))
}
getDirNextChunkPointer () {
start=$(( $1 * 512 ))
echo $(( $(readcharDec "$image" $(($start+2)) ) + \
$(readcharDec "$image" $(($start+3)) ) * 256 ))
}
# -- script begins in earnest here
copyFile () {
activeFileBytesCopied=0
storageType=$(getStorageType $1 $2)
keyPointer=$(getKeyPointer $1 $2)
fileLen=$(getFileLength $1 $2)
if (( $storageType == 1 )); then #seedling
copyBlock $keyPointer $fileLen
elif (( $storageType == 2 )); then #sapling
processIndexBlock $keyPointer
elif (( $storageType == 3 )); then #tree
processMasterIndexBlock $keyPointer
elif (( $storageType == 5)); then #forked fileLen
processForkedFile $keyPointer
fi
}
copyBlock () {
#arg1: block to copy
#arg2: bytes to write (should be 512, unless final block with less than 512 bytes)
#echo $1 $2 $activeFileBytesCopied
(( $1 == 0 )) && blockSource=/dev/zero || blockSource="$image"
if (( $resourceFork > 0 )); then
[[ $AD ]] && dd if="$blockSource" of="$ADdir/$targetName" bs=1 count=$2 skip=$(($1*512)) seek=$(($activeFileBytesCopied + 741)) 2> /dev/null
[[ $EX ]] && dd if="$blockSource" of="$targetDir/${eTargetName}r" bs=1 count=$2 skip=$(($1*512)) seek=$activeFileBytesCopied 2> /dev/null
else
dd if="$blockSource" of="$targetDir/$targetName" bs=1 count=$2 skip=$(($1*512)) seek="$activeFileBytesCopied" 2> /dev/null
fi
activeFileBytesCopied=$(( $activeFileBytesCopied + $2 ))
}
processDir () {
# arg1: dirBlock
# arg2/3/4/5: for non-key chunks: entryCount, entry#,
# workingDirName, processedEntryCount
local entryCount
local e
local pe
local workingDirName
if [[ $2 ]]; then
entryCount=$2
e=$3
workingDirName=$4
pe=$5
else
e=0
pe=0
entryCount=$(getDirEntryCount $1)
workingDirName=$(getWorkingDirName $1)
DIRPATH="$DIRPATH/$workingDirName"
if [[ $PDOSPATH_INDEX ]]; then
if (( $PDOSPATH_INDEX == 1 )); then
if [[ "/$PDOSPATH_SEGMENT" != "$DIRPATH" ]]; then
echo "ProDOS volume name does not match disk image."
exit 2
else
(( PDOSPATH_INDEX++ ))
PDOSPATH_SEGMENT=${PDOSPATH[PDOSPATH_INDEX]}
fi
fi
else
echo $DIRPATH
fi
fi
while (( $pe < $entryCount )); do
if (( $(getStorageType $1 $e) > 0 )); then
processEntry $1 $e
(( pe++ ))
fi
(( e++ ))
(( ($e + ( $e>11 ) ) % 13 )) || { processDir $(getDirNextChunkPointer $1) $entryCount $e $workingDirName $pe; break; }
done
}
processEntry () {
#echo $(getFileName $1 $2) $(getStorageType $1 $2) $(getFileType $1 $2) $(getKeyPointer $1 $2) $(getFileLength $1 $2) $(getAuxType $1 $2) $(getCreationDate $1 $2) $(getModifiedDate $1 $2)
activeFileName=$(getFileName $1 $2)
activeFileSize=$(getFileLength $1 $2)
[[ $PDOSPATH_INDEX ]] || echo " $activeFileName"
if [[ ( ! $PDOSPATH_INDEX ) || ( $activeFileName == $PDOSPATH_SEGMENT ) ]]; then
if (( $(getStorageType $1 $2) == 13 )); then
[[ $PDOSPATH_INDEX ]] || targetDir="$targetDir/$activeFileName"
ADdir="$targetDir/.AppleDouble"
[[ $DIR || -d $targetDir ]] || mkdir -p $targetDir
[[ $DIR || ! $AD || -d $ADdir ]] || mkdir -p $ADdir
if [[ $PDOSPATH_SEGMENT ]]; then
(( PDOSPATH_INDEX++ ))
PDOSPATH_SEGMENT=${PDOSPATH[PDOSPATH_INDEX]}
fi
processDir $(getKeyPointer $1 $2)
DIRPATH=${DIRPATH%/*}
[[ $PDOSPATH_INDEX ]] || targetDir="$targetDir/.."
ADdir="$targetDir/.AppleDouble"
else
[[ $DIR ]] && return
[[ $targetName ]] || targetName=$activeFileName
[[ $EX ]] && eTargetName="$targetName#$(getFileType $1 $2 | tr [:upper:] [:lower:])$(getAuxType $1 $2 | sed 's/\.//' | tr [:upper:] [:lower:])"
touch "$targetDir/$targetName"
makeADfile
copyFile $1 $2
creationDate=$(getCreationDate $1 $2);
modifiedDate=$(getModifiedDate $1 $2);
if [[ $creationDate == "NONE" && $modifiedDate != "NONE" ]]; then
creationDate=$modifiedDate
elif [[ $creationDate != "NONE" && $modifiedDate == "NONE" ]]; then
modifiedDate=$creationDate
elif [[ $creationDate == "NONE" && $modifiedDate == "NONE" ]]; then
creationDate=$(date "+%s")
modifiedDate=$creationDate
fi
if [[ $AD ]]; then # AppleDouble
# set dates
ADfilePath="$ADdir/$targetName"
writecharsHex "$ADfilePath" 637 $(unixDateToADDate $creationDate).$(unixDateToADDate $modifiedDate)
writecharHex "$ADfilePath" 645 80
writecharHex "$ADfilePath" 649 80
#set type/creator
writechars "$ADfilePath" 653 "p"
writecharsHex "$ADfilePath" 654 "$(getFileType $1 $2).$(getAuxType $1 $2)"
writechars "$ADfilePath" 657 "pdos"
fi
touch -d @$modifiedDate "$targetDir/$targetName"
if [[ $EX ]]; then # extended name
mv "$targetDir/$targetName" "$targetDir/$eTargetName"
[[ -f $targetDir/${eTargetName}r ]] && touch -d @$modifiedDate "$targetDir/${eTargetName}r"
fi
[[ $PDOSPATH_SEGMENT ]] && syncExit
targetName=
fi
#else
#echo "$activeFileName doesn't match $PDOSPATH_SEGMENT"
fi
}
processForkedFile () {
# finder info except type/creator
fInfoA_entryType=$(readcharDec "$image" 9)
fInfoB_entryType=$(readcharDec "$image" 27)
if (( $fInfoA_entryType==1 )); then
readchars "$image" 18 8 | writechars "$image" 661
elif (( $fInfoA_entryType==2 )); then
readchars "$image" 10 16 | writechars "$image" 669
fi
if (( $fInfoB_entryType==1 )); then
readchars "$image" 36 8 | writechars "$image" 661
elif (( $fInfoB_entryType==2 )); then
readchars "$image" 28 16 | writechars "$image" 669
fi
for f in 0 256; do
resourceFork=$f
activeFileBytesCopied=0
forkStart=$(( ($1 * 512) )) # start of Forked File key block
# echo --$forkStart
forkStorageType=$(readcharDec "$image" $(($forkStart+$f+0)) )
forkKeyPointer=$(( $(readcharDec "$image" $(($forkStart+$f+1)) ) + \
$(readcharDec "$image" $(($forkStart+$f+2)) ) * 256 ))
forkFileLen=$(( $(readcharDec "$image" $(($forkStart+$f+5)) ) + \
$(readcharDec "$image" $(($forkStart+$f+6)) ) * 256 + \
$(readcharDec "$image" $(($forkStart+$f+7)) ) * 65536 ))
activeFileSize=$forkFileLen
if (( $resourceFork > 0 )); then
rsrcForkLenHex=$(readcharHex "$image" $(($forkStart+$f+7)) ).\
$(readcharHex "$image" $(($forkStart+$f+6)) ).\
$(readcharHex "$image" $(($forkStart+$f+5)) )
# echo ">>>$rsrcForkLenHex"
echo " [resource fork]"
[[ $AD ]] && writecharsHex "$ADdir/$targetName" 35 "$rsrcForkLenHex"
else
echo " [data fork]"
fi
if (( $forkStorageType == 1 )); then #seedling
copyBlock $forkKeyPointer $forkFileLen
elif (( $forkStorageType == 2 )); then #sapling
processIndexBlock $forkKeyPointer
elif (( $forkStorageType == 3 )); then #tree
processMasterIndexBlock $forkKeyPointer
fi
done
# echo
resourceFork=0
}
processMasterIndexBlock() {
processIndexBlock $1 1
}
processIndexBlock () {
#arg1: indexBlock
#arg2: if set, it's a Master Index Block
local pos=0
local bytesRemaining
while (( $activeFileBytesCopied < $activeFileSize )); do
targetBlock=$(( $(readcharDec $image $(($1*512+$pos)) ) + $(readcharDec $image $(($1*512+($pos+256) )) )*256 ))
if [[ $2 ]]; then
processIndexBlock $targetBlock
else
bytesRemaining=$(($activeFileSize - $activeFileBytesCopied))
bs=$(( $bytesRemaining<512 ? $bytesRemaining : 512 ))
copyBlock $targetBlock $bs
fi
(( pos++ ))
(( $pos > 255 )) && break # go to next entry in Master Index Block (tree)
done
}
makeADfile () {
ADfilePath="$ADdir/$targetName"
[[ ! $AD ]] && return
dd if=/dev/zero of="$ADfilePath" bs=741 count=1 2> /dev/null
writecharsHex "$ADfilePath" $(hexToDec 00) "00.05.16.07.00.02.00.00" # ADv2 header
writecharsHex "$ADfilePath" $(hexToDec 18) "00.0D" # number of entries
writecharsHex "$ADfilePath" $(hexToDec 1A) "00.00.00.02.00.00.02.E5.00.00.00.00" # Resource Fork
writecharsHex "$ADfilePath" $(hexToDec 26) "00.00.00.03.00.00.00.B6.00.00.00.00" # Real Name
writecharsHex "$ADfilePath" $(hexToDec 32) "00.00.00.04.00.00.01.B5.00.00.00.00" # Comment
writecharsHex "$ADfilePath" $(hexToDec 3E) "00.00.00.08.00.00.02.7D.00.00.00.10" # Dates Info
writecharsHex "$ADfilePath" $(hexToDec 4A) "00.00.00.09.00.00.02.8D.00.00.00.20" # Finder Info
writecharsHex "$ADfilePath" $(hexToDec 56) "00.00.00.0B.00.00.02.C1.00.00.00.08" # ProDOS file info
writecharsHex "$ADfilePath" $(hexToDec 62) "00.00.00.0D.00.00.02.B5.00.00.00.00" # AFP short name
writecharsHex "$ADfilePath" $(hexToDec 6E) "00.00.00.0E.00.00.02.B1.00.00.00.04" # AFP File Info
writecharsHex "$ADfilePath" $(hexToDec 7A) "00.00.00.0F.00.00.02.AD.00.00.00.04" # AFP Directory ID
# dbd (second time) will create DEV, INO, SYN, SV~
}
syncExit () {
if [[ -d /usr/local/etc/netatalk && $AD ]]; then
echo "File(s) have been copied to the target directory. If the directory" 1>&2
echo "is shared by Netatalk, please use 'afpsync' now." 1>&2
fi
exit 0
}
usage () {
echo "usage:"
echo "copy all files: cppo [-ad|-e] imagefile targetDirectory"
echo "copy one file : cppo [-ad|-e] imagefile /FULL/PRODOS/FILE/PATH targetPath"
echo "catalog image : cppo -cat imagefile"
echo
echo "cppo copies either one file or all files from a ProDOS raw disk image"
echo "to a folder shared by Netatalk. -cat displays all files on the image."
echo "No verification or validation of the disk image is performed."
echo
echo "-ad enables creating AppleDouble header files and copying resource forks."
echo "-e appends the ProDOS type and auxtype to filenames, and copies resource"
echo " forks, so they can be preserved when added to ShrinkIt archives by"
echo " nulib2 (using its -e option)."
echo
echo "Wildcard matching (*) is not supported."
exit 1
}
# --- start
if [[ $1 == "-ad" ]]; then
AD=1
shift
fi
if [[ $1 == "-e" ]]; then
[[ $AD ]] && usage
EX=1
shift
fi
if [[ $1 == "-cat" ]]; then
DIR=1
shift
fi
[[ ( $DIR && $1 ) || ( $1 && $2 ) ]] || usage
[[ $3 && ( ${2:0:1} != "/" ) && ( ${2:0:1} != ":" ) ]] && usage
image="$1"
[[ -f "$image" ]] || { echo "Source image not found."; exit 2; }
if [[ $3 ]]; then
pdospath=$(echo $2 | tr [:lower:] [:upper:])
targetPath=$3
if [[ -d $targetPath ]]; then
targetDir=$targetPath
else
targetDir="${targetPath%/*}"
targetName="${targetPath##*/}"
fi
[[ -d $targetDir ]] || { echo "Target directory not found."; exit 2; }
else
if [[ ! $DIR ]]; then
[[ -d "$2" ]] || { echo "Target directory not found."; exit 2; }
fi
fi
activeDirBlock=0
activeFileName=""
activeFileSize=0
activeFileBytesCopied=0
resourceFork=0
if [[ $3 ]]; then
IFS='/:'
PDOSPATH=($pdospath)
unset IFS
[[ ! ${PDOSPATH[0]} ]] && (( PDOSPATH_INDEX++ ))
PDOSPATH_SEGMENT=${PDOSPATH[PDOSPATH_INDEX]}
ADdir="$targetDir/.AppleDouble"
[[ ! $AD || -d $ADdir ]] || mkdir $ADdir
processDir 2
echo "ProDOS file not found within image file."
exit 2
else
if [[ ! $DIR ]]; then
targetDir="$2/$(getVolumeName)"
ADdir="$targetDir/.AppleDouble"
[[ -d $targetDir ]] || mkdir -p $targetDir
[[ ! $AD || -d $ADdir ]] || mkdir -p $ADdir
fi
processDir 2
[[ $DIR ]] || syncExit
fi