449 lines
10 KiB
Bash
Executable File
449 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
## Copyright Ian Jauslin 2015-2023
|
|
##
|
|
## Licensed under the Apache License, Version 2.0 (the "License");
|
|
## you may not use this file except in compliance with the License.
|
|
## You may obtain a copy of the License at
|
|
##
|
|
## http://www.apache.org/licenses/LICENSE-2.0
|
|
##
|
|
## Unless required by applicable law or agreed to in writing, software
|
|
## distributed under the License is distributed on an "AS IS" BASIS,
|
|
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
## See the License for the specific language governing permissions and
|
|
## limitations under the License.
|
|
|
|
# directory containing the engine files
|
|
enginedir=/usr/share/BBlog/engines
|
|
|
|
# version
|
|
version=2.2.1
|
|
|
|
function print_config {
|
|
echo "engine: $engine"
|
|
echo "db: $db"
|
|
echo "conf: $conf"
|
|
echo "aux: $aux"
|
|
echo "format: $format"
|
|
echo "outbib: $outbib"
|
|
echo "order: $order"
|
|
echo "aux_cmd: $aux_cmd"
|
|
echo "ref_map: $ref_map"
|
|
}
|
|
|
|
# defaults
|
|
function set_defaults {
|
|
engine="sqlite"
|
|
db=""
|
|
conf=""
|
|
aux='*.aux'
|
|
format='\bibitem[%citeref%]{%token%}%n%%author% - {\it %title%}, %journal%, %year%.%n%'
|
|
outbib=''
|
|
order="alphabetical"
|
|
aux_cmd='\\citation{'
|
|
ref_map=''
|
|
}
|
|
|
|
# read cli args
|
|
function read_cli {
|
|
|
|
flag=""
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
"-e" )
|
|
flag="engine" ;;
|
|
"-d" )
|
|
flag="db" ;;
|
|
"-c" )
|
|
flag="conf" ;;
|
|
"-a" )
|
|
flag="aux" ;;
|
|
"-f" )
|
|
flag="format" ;;
|
|
"-b" )
|
|
flag="outbib" ;;
|
|
"-o" )
|
|
flag="order" ;;
|
|
"-A" )
|
|
flag="aux_cmd" ;;
|
|
"-F" )
|
|
flag="filter" ;;
|
|
"-r" )
|
|
flag="ref_map" ;;
|
|
# version
|
|
"-v" )
|
|
echo "$version"; exit ;;
|
|
* )
|
|
if [ -n "$flag" ]; then
|
|
# treat filters differently
|
|
if [ "$flag" != 'filter' ]; then
|
|
eval "$flag='$arg'"
|
|
# flag the variable as set
|
|
eval "set_$flag=1"
|
|
else
|
|
field="${arg%%:*}"
|
|
value="${arg#*:}"
|
|
eval "filter_$field='$value'"
|
|
eval "set_filter_$field=1"
|
|
fi
|
|
flag=""
|
|
fi
|
|
esac
|
|
done
|
|
}
|
|
|
|
# read config file
|
|
function read_config {
|
|
extra_entries_index=0
|
|
|
|
if [ -n "$conf" ]; then
|
|
[ -f "$conf" ] || { echo "error: $conf: no such file" 1>&2 ; exit -1 ; }
|
|
while read -r confline; do
|
|
flag=""
|
|
field="${confline%%:*}"
|
|
value="${confline#*:}"
|
|
# remove spaces
|
|
field=$(echo "$field" | sed -r 's/ *$//')
|
|
value=$(echo "$value" | sed -r 's/^ *//')
|
|
case "$field" in
|
|
"engine" )
|
|
flag="engine" ;;
|
|
"database" )
|
|
flag="db" ;;
|
|
"aux" )
|
|
flag="aux" ;;
|
|
"format" )
|
|
flag="format" ;;
|
|
"out_file" )
|
|
flag="outbib" ;;
|
|
"order" )
|
|
flag="order" ;;
|
|
"aux_cmd" )
|
|
flag="aux_cmd" ;;
|
|
"ref_map" )
|
|
flag="ref_map" ;;
|
|
"filter" )
|
|
# set flag to the specific filter
|
|
flag="filter_${value%%:*}"
|
|
# reset value
|
|
value="${value#*:}" ;;
|
|
"extra" )
|
|
extra[$extra_entries_index]="$value"
|
|
extra_entries_index=$((extra_entries_index+1)) ;;
|
|
esac
|
|
|
|
if [ -n "$flag" ]; then
|
|
# check it was not set on the command line
|
|
eval "[ -n \"\$set_$flag\" -a \"\$set_$flag\" = 1 ] || $flag='$value'"
|
|
fi
|
|
done <"$conf"
|
|
|
|
# sort extra entries
|
|
if [ "$extra_entries_index" -gt 0 -a "$order" = "alphabetical" ]; then
|
|
IFS=$'\n' extra=($(echo "${extra[*]}" | sort -k 1,1 -t :))
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# check the configuration
|
|
function check_config {
|
|
# check whether the engine exists
|
|
[ -e "$enginedir/$engine.sh" ] || { echo "error: engine $engine not found in $enginedir" 1>&2; exit -1 ; }
|
|
|
|
# check the database and aux file
|
|
for file in "$db" "$aux"; do
|
|
[[ "$file" =~ '*' || -e "$file" ]] || { echo "error: $file: no such file" 1>&2 ; exit -1 ; }
|
|
done
|
|
|
|
# check the $order variable
|
|
[ "$order" != "alphabetical" -a "$order" != "appearance" ] && { echo "error: order should be one of 'alphabetical' or 'appearance' (read $order)" 1>&2 ; exit -1 ; }
|
|
|
|
# check for ref_map
|
|
[ -n "$ref_map" ] && [ ! -e "$ref_map" ] && { echo "error: $ref_map: no such file" 1>&2 ; exit -1 ; }
|
|
}
|
|
|
|
function replace_format {
|
|
out="$1"
|
|
|
|
# replace newlines
|
|
out="${out//\%n\%/\\n}"
|
|
|
|
# if the ref of the entry has been mapped, fetch the old citeref
|
|
if [ -n "$ref_map" ]; then
|
|
citeref=$(inverse_map_citeref "$citeref")
|
|
fi
|
|
|
|
# first search for conditionals
|
|
# while '|' is still in $out
|
|
while : ; do
|
|
# find next '|'
|
|
tmp=${out#*|}
|
|
# save text before '|'
|
|
pre=${out%%|*}
|
|
# check whether it was found
|
|
[ "$tmp" = "$out" ] && break
|
|
# find next '|'
|
|
command=${tmp%%|*}
|
|
# save text after '|'
|
|
post=${tmp#*|}
|
|
# check whether it was found
|
|
[ "$command" = "$tmp" ] && { echo "error: '|'s do not match in '$1'" 1>&2 ; exit -1 ; }
|
|
|
|
# extract field
|
|
field="${command%%\?*}"
|
|
repl="${command##*\?}"
|
|
|
|
# check whether field is empty
|
|
val=$(eval "echo \$$field")
|
|
if [ -n "$val" ]; then
|
|
out="$pre$repl$post"
|
|
else
|
|
out="$pre$post"
|
|
fi
|
|
done
|
|
|
|
# replace '%'
|
|
# while '%' is still in $out
|
|
while : ; do
|
|
# find next '%'
|
|
tmp=${out#*%}
|
|
# check whether it was found
|
|
[ "$tmp" = "$out" ] && break
|
|
# find next '%'
|
|
command=${tmp%%%*}
|
|
# check whether it was found
|
|
[ "$command" = "$tmp" ] && { echo "error: '%'s do not match in '$1'" 1>&2 ; exit -1 ; }
|
|
|
|
# the field entry may be followed by a filter
|
|
field="${command%:*}"
|
|
# filter
|
|
filter="${command##*:}"
|
|
|
|
# apply filter
|
|
if [ -n "$filter" ]; then
|
|
# replace '%' inside filter text
|
|
sed_cmd=$(replace_format "$(eval echo \$filter_$filter)")
|
|
replacement=$(eval "echo \$$field" | sed -r "$sed_cmd" )
|
|
else
|
|
replacement=$(eval "echo \$$field")
|
|
fi
|
|
|
|
# escape '%' in replacement
|
|
replacement="${replacement//\%/::iansays:percent::}"
|
|
|
|
|
|
out="${out//\%$command\%/$replacement}"
|
|
done
|
|
|
|
# finish replacing newlines
|
|
out="${out//\\n/%}"
|
|
out=$(echo "$out" | tr "%" "\n")
|
|
|
|
# un-escape '%' in replacement
|
|
out="${out//::iansays:percent::/\%}"
|
|
|
|
echo "$out"
|
|
}
|
|
|
|
function escape_string {
|
|
str="$1"
|
|
str="${str//\\/\\\\}"
|
|
str="${str//\`/\\\`}"
|
|
str="${str//\"/\\\"}"
|
|
str="${str//\$/\\\$}"
|
|
echo "$str"
|
|
}
|
|
|
|
# replace a citeref according to a ref_map
|
|
function map_citeref {
|
|
# check whether a ref_map was specified
|
|
[ -z "$ref_map" ] && { echo "$1" ; return 0 ; }
|
|
|
|
oldref="$1"
|
|
newref=$(echo "$oldref" | sed -r -f "$ref_map")
|
|
echo "$newref"
|
|
}
|
|
|
|
# find the pre-image of a citeref through the ref_map
|
|
function inverse_map_citeref {
|
|
# check whether a ref_map was specified
|
|
[ -z "$ref_map" ] && { echo "$1" ; return 0 ; }
|
|
|
|
newref="$1"
|
|
|
|
foundref=0
|
|
# sift through aux file
|
|
grep -h "$aux_cmd" $aux | while read -r refs; do
|
|
eval "refs=\${refs#$aux_cmd}"
|
|
refs="${ref%\}}"
|
|
# can be a comma separated list
|
|
for ref in $(echo -n "$refs" | tr ',' '\n'); do
|
|
# replace the ref via the ref_map
|
|
possibleref=$(map_citeref "$ref")
|
|
# check whether the ref is the right one
|
|
if [ "$possibleref" = "$newref" ]; then
|
|
echo "$ref"
|
|
foundref=1
|
|
return 1
|
|
fi
|
|
done
|
|
done && echo "$foundref$newref"
|
|
}
|
|
|
|
# format entries
|
|
function format_BBlog_entries {
|
|
# keep track of tokens to avoid repetitions
|
|
token_list=""
|
|
|
|
has_there_been_a_nonempty_line_so_far=0
|
|
|
|
while read -r line; do
|
|
# if line has 0 size, then new entry
|
|
if [ -z "$line" ]; then
|
|
# skip if it is the first line
|
|
if [ $has_there_been_a_nonempty_line_so_far = 1 ]; then
|
|
# check whether an extra entry precedes it
|
|
# alphabetical order
|
|
if [ "$order" = "alphabetical" ]; then
|
|
old_token="$token"
|
|
old_citeref="$citeref"
|
|
while [[ ${#extra} -gt 0 && "${extra[0]%%:*}" < "$prauth$year" ]]; do
|
|
reftokval="${extra[0]#*:}"
|
|
citeref="${reftokval%%:*}"
|
|
tokval="${reftokval#*:}"
|
|
token="${tokval%%:*}"
|
|
value="${tokval#*:}"
|
|
# check whether token is unique
|
|
token=$(replace_repeated_token "$token" "$token_list")
|
|
# add token to list
|
|
token_list="$token_list;$token"
|
|
|
|
replace_format "$value"
|
|
|
|
IFS=$'\n' extra=($(echo "${extra[*]:1}"))
|
|
done
|
|
token="$old_token"
|
|
citeref="$old_citeref"
|
|
|
|
# check whether token is unique
|
|
token=$(replace_repeated_token "$token" "$token_list")
|
|
# add token to list
|
|
token_list="$token_list;$token"
|
|
|
|
replace_format "$format"
|
|
|
|
# appearance order
|
|
else
|
|
foundit=0
|
|
if [ ${#extra} -gt 0 ]; then
|
|
for entry in "${extra[@]}"; do
|
|
ref="${entry#*:}"
|
|
ref="${ref%%:*}"
|
|
if [ "$ref" = "$citeref" ]; then
|
|
old_token="$token"
|
|
old_citeref="$citeref"
|
|
|
|
citeref="$ref"
|
|
token="${entry#*:*:}"
|
|
value="${token#*:}"
|
|
token="${token%%:*}"
|
|
# check whether token is unique
|
|
token=$(replace_repeated_token "$token" "$token_list")
|
|
# add token to list
|
|
token_list="$token_list;$token"
|
|
|
|
replace_format "$value"
|
|
|
|
token="$old_token"
|
|
citeref="$old_citeref"
|
|
foundit=1
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ $foundit = 0 ]; then
|
|
# check whether token is unique
|
|
token=$(replace_repeated_token "$token" "$token_list")
|
|
# add token to list
|
|
token_list="$token_list;$token"
|
|
|
|
replace_format "$format"
|
|
fi
|
|
fi
|
|
fi
|
|
has_there_been_a_nonempty_line_so_far=0
|
|
else
|
|
eval "$line"
|
|
has_there_been_a_nonempty_line_so_far=1
|
|
fi
|
|
done
|
|
|
|
# add remaining extra entries (only for alphabetical order)
|
|
if [ "$order" = "alphabetical" ]; then
|
|
while [ ${#extra} -gt 0 ]; do
|
|
reftokval="${extra[0]#*:}"
|
|
citeref="${reftokval%%:*}"
|
|
tokval="${reftokval#*:}"
|
|
token="${tokval%%:*}"
|
|
value="${tokval#*:}"
|
|
# check whether token is unique
|
|
token=$(replace_repeated_token "$token" "$token_list")
|
|
# add token to list
|
|
token_list="$token_list;$token"
|
|
|
|
replace_format "$value"
|
|
IFS=$'\n' extra=($(echo "${extra[*]:1}"))
|
|
done
|
|
fi
|
|
}
|
|
|
|
# replace a token that already exists
|
|
function replace_repeated_token {
|
|
token="$1"
|
|
list="$2"
|
|
|
|
out="$token"
|
|
i=0
|
|
while $(echo "$token_list;" | grep -q ";$out;"); do
|
|
i=$((i+1))
|
|
out="$token"$(alpha_number $i)
|
|
done
|
|
|
|
echo "$out"
|
|
}
|
|
|
|
# convert an integer to base 26
|
|
function alpha_number {
|
|
num="$1"
|
|
rem="$num"
|
|
out=""
|
|
letters=(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)
|
|
while [ "$rem" -gt 0 ]; do
|
|
digit=$((rem-26*(rem/26)))
|
|
out="${letters[$digit]}$out"
|
|
rem=$(((rem-digit)/26))
|
|
done
|
|
|
|
echo "$out"
|
|
}
|
|
|
|
|
|
set_defaults
|
|
read_cli "$@"
|
|
read_config
|
|
check_config
|
|
source "$enginedir/$engine.sh"
|
|
|
|
# check whether to order by alphabetical order or appearance
|
|
[ "$order" = "alphabetical" ] && fetch_cmd=fetch_BBlog_entries_alpha || fetch_cmd=fetch_BBlog_entries_appearance
|
|
|
|
# fetch entries
|
|
if [ -n "$outbib" ]; then
|
|
"$fetch_cmd" | format_BBlog_entries > "$outbib"
|
|
else
|
|
"$fetch_cmd" | format_BBlog_entries
|
|
fi
|
|
|