#!/bin/bash # Currency conversion rates list generator. # Uses data from units_cur and ISO currency code table to format currency rates. # # By The Free Thinker, 2020. # V. 1 # TODO (maybe): Generate currency lists for multiple base currencies. # 1 = Enable generating rates list LISTENABLE=0 # 1 = Enable generating gophermaps and directories GOPHERENABLE=0 # Files needed for Gopher mode: # menuhead.txt - prepended to currency menu gophermap # gopherconvert.sh - Script that is sourced by gophermap scripts in currency directory to perform # conversion and format output. Called with parameters for currency conversion. # - Script location is specified below. # [CUR]/head.txt - File in each currency directory with a custom header prepended to currency output. # Generated by script to state currency name and code, if not already existant. # # TODO - Override above with command-line options? #Path to gopherconvert.sh script: GOPHERCONVSCRIPT="/home/freet/scripts/currconv/gopherconvert.sh" # URL for fetching currency name file in [code],[name] format: CURRENCYNAMEURL="https://raw.githubusercontent.com/umpirsky/currency-list/master/data/en_AU/currency.csv" UPDATENAMES=1 #Number of decimal places that rate change is shown to: CHANGESCALE=6 # Set to "0" to be quiet: VERBOSITY=1 #Custom Currency Array (for currencies without official ISO 4217 code) #Format is: '[code],"[name]",[ID]\n' # [code] is a 3-letter code to stand in for an official ISO 4217 currency code. # [name] is the display name of the currency, quote to include spaces, can't contain ",". # [ID] is the identifier used in currency.units. declare -a CUSTOMCURR=(\ 'XBT,"Bitcoin (BTC/XBT)",bitcoin\n'\ ) ############################################################# #################### END OF CONFIGURATION ################### ############################################################# #CURRRATE Arrays: declare -a CURRRATE #Conversion rate eg. "0.40810863746164" declare -a CURRRATEID #Currency identifier eg. "tongapa'anga" declare -a CURRRATECODE #Base currency eg. "euro" #CURRID Arrays: declare -a CURRID #Currency identifier eg. "tongapa'anga" declare -a CURRIDCODE #ISO 4217 currency code eg. "TOP" OLDIFS=$IFS ### Process command-line options (currently just for convert option): if [ "$1" == "convert" ] then CONVERT=1 LISTENABLE=0 # Disable currency list output whenever doing a conversion GOPHERENABLE=0 # Also disable Gopher stuff CONVAMOUNT="`expr "$2" : '\([0-9]*\|[0-9]*\.[0-9]*\)[a-zA-Z]*'`" if [ $? -gt 0 ] then echo -e "ERROR: Invalid Input Amount\n\ Usage is: currencyconv.sh [convert [amount][input 3-letter currency code] [output 3-letter currency code] ]" exit 1 fi CONVINCODE="`echo ${2:(-3)} | tr '[:lower:]' '[:upper:]'`" CONVOUTCODE="`echo $3 | tr '[:lower:]' '[:upper:]'`" echo -e "From: $CONVINCODE\nTo: $CONVOUTCODE" elif [ "$1" ] then echo -e "ERROR: Option not recognised.\n\ Usage is: currencyconv.sh [convert [amount][input 3-letter currency code] [output 3-letter currency code] ]" exit 1 else CONVERT=0 fi ### Download latest currency names file if it has changed: ####TODO: Handle 404, etc., errors. if [ $UPDATENAMES -gt 0 ] then [ $VERBOSITY -gt 0 ] && echo "Checking for updated currency names..." if [ ! -e currency.names.etag ] then [ $VERBOSITY -gt 0 ] && echo "Can not find etag file - First run?" wget -q -S -O currency.names $CURRENCYNAMEURL 2>&1 | sed -n 's/ *ETag: \(.*\)$/\1/p' > currency.names.etag else ETAG="`cat currency.names.etag`" wget -v -S -O currency.names.new --header="If-None-Match: $ETAG" $CURRENCYNAMEURL 2>&1 | sed -n 's/ *ETag: \(.*\)$/\1/p' > currency.names.etag if [ -s currency.names.new ] then [ $VERBOSITY -gt 0 ] && echo "Currency names updated." mv currency.names.new currency.names fi fi fi IFS="," ###Add custom currencies to start of CURRID arrays: for (( COUNT = 0 ; COUNT < ${#CUSTOMCURR[*]} ; COUNT++ )) do CURRID[COUNT]="`echo -n -e "${CUSTOMCURR[COUNT]}" | cut -d "," -f 3`" CURRIDCODE[COUNT]="`echo -n -e "${CUSTOMCURR[COUNT]}" | cut -d "," -f 1`" done IFS=" " ### Read currency codes from Units rates file grep -E '^[A-Z]{3} +[a-zA-Z$]+$' currency.units > currency.units.tmp exec 3<> currency.units.tmp while read -r CODE ID <&3 do CURRID[COUNT]="$ID" CURRIDCODE[COUNT]="$CODE" (( COUNT++ )) done exec 3>&- ### Read currency rates from Units rates file COUNT=0 grep -E '^[a-z]+ +[0-9]' currency.units > currency.units.tmp exec 3<> currency.units.tmp while read -r ID RATE CODE <&3 do #Convert rate from scientific notation to suit bc (eg. "e-05" = "*10^05") # Copied from: https://stackoverflow.com/questions/12882611/how-to-get-bc-to-handle-numbers-in-scientific-aka-exponential-notation RATE=${RATE/e/*10^}; RATE=${RATE/^+/^} #Don't really know if the e+[num] form will crop up, playing safe. if [[ "$RATE" == 1'|'* ]]; then RATE="`expr "$RATE" : '.*|\([0-9.]*\)'`" #Strip "1|" bit from rate string RATE="`echo "scale=15; 1 / $RATE" | bc`" #Divide by one using bc (supports floating point calculations) fi CURRRATEID[COUNT]="$ID" CURRRATE[COUNT]="$RATE" CURRRATECODE[COUNT]="$CODE" (( COUNT++ )) done exec 3>&- rm currency.units.tmp if [ $LISTENABLE -gt 0 ] then # Output Units rates file update time echo -n "Currency data last updated" > currencyrates.new grep -E -o ' on [0-9]{4}-[0-9]{2}-[0-9]{2}$' currency.units >> currencyrates.new echo >> currencyrates.new elif [ $CONVERT -gt 0 ] then RATESUPDATEDATE="`grep -E -o ' on [0-9]{4}-[0-9]{2}-[0-9]{2}$' currency.units`" fi if [ $GOPHERENABLE -gt 0 ] then [ -f menuhead.txt ] && cp menuhead.txt gophermap.new || echo "menuhead.txt not found" # Output Units rates file update time echo -n " Currency data last updated" >> gophermap.new grep -E -o ' on [0-9]{4}-[0-9]{2}-[0-9]{2}$' currency.units >> gophermap.new [ -d gopherconvert_links ] && rm -f gopherconvert_links/* || mkdir gopherconvert_links #Create links to the binaries used by the gopherconvert.sh Bash script that runs in restricted mode declare -a BIN=( expr tr cat units ) #All commands used that aren't Bash built-ins for (( i = 0 ; i < ${#BIN[*]} ; i++ )) do LOCATION="`whereis -b ${BIN[i]}`" LEN=$[ ${#bin[i]} + 2 ] ln -s "`expr \"$LOCATION\" : \".* \(/.*bin/${BIN[i]}\)\"`" gopherconvert_links/ done rm -f customcurr.csv for (( i = 0 ; i < ${#CUSTOMCURR[*]} ; i++ )) do echo "${CUSTOMCURR[i]}" >> customcurr.csv done # ln -s "$GOPHERCONVSCRIPT" gopherconvert_links/gophermap # -- Gophernicus can't figure links out fi ### Read currency names and produce output #### TODO: Optimise to avoid checking IDs that have already been matched. [ $VERBOSITY -gt 0 ] && echo -n "Processed: " LINE=1 IFS="," echo -n -e "${CUSTOMCURR[*]}" | sort currency.names - | while read -r CODE NAME ID do NAME="`echo "$NAME" | tr -d '"'`" # Strip any quotes from around currency name # Find matching currency code in Units data, to reveal the index number of the ID string IDCOUNT=0 while [ "${CURRIDCODE[IDCOUNT]}" != "" ] && [ "${CURRIDCODE[IDCOUNT]}" != "$CODE" ] ; do (( IDCOUNT++ )) done if [ "${CURRIDCODE[IDCOUNT]}" != "" ] then LINE=$[ $LINE + 2 ] #Increment count of lines in currencyrates.new # Find currency rate associated with extracted ID string RATECOUNT=0 while [ "${CURRRATEID[RATECOUNT]}" != "${CURRID[IDCOUNT]}" ] && [ $RATECOUNT -lt ${#CURRRATEID[*]} ] do (( RATECOUNT++ )) done [ $VERBOSITY -gt 0 ] && echo -n " ${CURRIDCODE[IDCOUNT]} " ### Convert currency if [ $CONVERT -gt 0 ] then #Grab the needed data as it passes by: if [ "$CODE" == "$CONVINCODE" ] then CONVINNAME="$NAME" CONVINRATE="${CURRRATE[RATECOUNT]}" CONVINBASE="${CURRRATECODE[RATECOUNT]}" [[ "$CONVINBASE" == "US\$"* ]] && CONVINBASE=USD elif [ "$CODE" == "$CONVOUTCODE" ] then CONVOUTNAME="$NAME" CONVOUTRATE="${CURRRATE[RATECOUNT]}" CONVOUTBASE="${CURRRATECODE[RATECOUNT]}" [[ "$CONVOUTBASE" == "US\$"* ]] && CONVOUTBASE=USD fi # Once we've got everything, time for some output: if [ $CONVINNAME ] && [ $CONVOUTNAME ] then if [ "$CONVINBASE" != "$CONVOUTBASE" ] && [ "$CONVINBASE" ] && [ "$CONVOUTBASE" ] then echo -e "\n\nSorry, the currency data uses different base currencies for the currencies that you want to convert \ between, and converting between different base currencies is just too confusing for this poor little script.\n\n\ I'm sure that GNU Units would be happy to help, if you have it handy..." #' elif [ "$CONVINCODE" == "$CONVOUTBASE" ] then echo -n -e "\n\n Result: "; printf "%.4f\n" "`echo "scale=15; $CONVAMOUNT / $CONVOUTRATE" | bc`" elif [ "$CONVOUTCODE" == "$CONVINBASE" ] then echo -n -e "\n\n Result: "; printf "%.4f\n" "`echo "scale=15; $CONVAMOUNT * $CONVINRATE" | bc`" else echo -n -e "\n\n Result: "; printf "%.4f\n" "`echo 'scale=15; ('$CONVAMOUNT' * '$CONVINRATE') / '$CONVOUTRATE | bc`" fi echo -e " ------------------------------------------------------------------\n\ Input currency name: $CONVINNAME\tRATE: $CONVINRATE $CONVINBASE\n\ Output currency name: $CONVOUTNAME\tRATE: $CONVOUTRATE $CONVOUTBASE\n\ Currency rates last updated$RATESUPDATEDATE" exit 0 fi fi ###Output rates table in alphabetical order of currency codes ### Write currency code, name from currency.names, and rate, to currencyrates file: if [ $LISTENABLE -gt 0 ] then CHANGESTRING= #Rise/Fall Indicator #Output rate change since last run, if currency list (and base currency) not changed: if [ -r currencyrates.txt ] && expr "`sed -n "$LINE p" currencyrates.txt`" : "${CURRIDCODE[IDCOUNT]}" > /dev/null #Check currency codes match then OLDRATE="`sed -n "$[ $LINE + 1 ] p" currencyrates.txt`" # Grab line with currency rate if expr "$OLDRATE" : '.*'"${CURRRATECODE[RATECOUNT]}"'.*' > /dev/null # Check that base currencies match then OLDRATE="`expr "$OLDRATE" : ' *\([0-9.^+*-]*\)'`" #Get old currency rate if [ $OLDRATE ] #Base currency won't have a rate number, so skip it. then # echo "${CURRRATE[RATECOUNT]} - $OLDRATE" CHANGE="`printf "%.${CHANGESCALE}f" \`echo "scale=15; ${CURRRATE[RATECOUNT]} - $OLDRATE" | bc\``" #Calculate change in currency rate if [ "$CHANGE" == "`printf "%.${CHANGESCALE}f"`" ] then #If currency is steady, mark with "-" CHANGESTRING=' ( - )' else #Otherwise mark "^" or "v" depending on whether number is marked negative: [[ "$CHANGE" != '-'* ]] && CHANGESTRING=' (^ '$CHANGE')' || CHANGESTRING=' (v '$CHANGE')' fi fi fi fi #Output line: echo -e "${CURRIDCODE[IDCOUNT]} - $NAME:\n ${CURRRATE[RATECOUNT]} ${CURRRATECODE[RATECOUNT]}$CHANGESTRING" >> currencyrates.new # echo -n "Line $LINE: " ; sed -n "$LINE p" currencyrates.txt fi ###Generate Gophermap and Gopher currency directories if [ $GOPHERENABLE -gt 0 ] then # Create and populate currency directory if it's not already there: if [ ! -d "$CODE" ] then mkdir "$CODE" echo -e '#!/bin/bash\nPATH= . '"$GOPHERCONVSCRIPT" > "$CODE/gophermap" #echo -e '#!/bin/bash\n. ../gopherconvert.sh $QUERY_STRING' > "$CODE/gophermap" #For old/alternative gopherconvert.sh chmod a+rx,go-w "$CODE/gophermap" echo -e " --- Converted to $NAME ($CODE) --- \n" > "$CODE/head.txt" fi # Copy fresh links to executables used by gopherconvert.sh so that restricted Bash can find them: cp -d gopherconvert_links/* "$CODE/" # Add currency to menu gophermap: echo -e "7$CODE - $NAME:\t$CODE\n ${CURRRATE[RATECOUNT]} ${CURRRATECODE[RATECOUNT]}$CHANGESTRING" >> gophermap.new fi fi done [ $VERBOSITY -gt 0 ] && echo [ $LISTENABLE -gt 0 ] && mv currencyrates.new currencyrates.txt [ $GOPHERENABLE -gt 0 ] && mv gophermap.new gophermap IFS="$OLDIFS"