summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/control.sh192
1 files changed, 107 insertions, 85 deletions
diff --git a/src/control.sh b/src/control.sh
index f7c4bcf..283e3d4 100644
--- a/src/control.sh
+++ b/src/control.sh
@@ -2,7 +2,7 @@
# src/control.sh
# Functions for parsing control files.
#
-# Copyright (C) 2012, 2013 Patrick McDermott
+# Copyright (C) 2012, 2018, 2019 Patrick McDermott
#
# This file is part of the ProteanOS Archive Manager.
#
@@ -20,115 +20,137 @@
# along with the ProteanOS Archive Manager. If not, see
# <http://www.gnu.org/licenses/>.
-control_file=
-control_line_nr=
+_parse_control_error()
+{
+ file="${1}"
+ line_nr="${2}"
+ msg_id="${3}"
+ shift 3
+ local file_info=
+
+ if [ ${line_nr} -eq 0 ]; then
+ file_info="$(printf '%s' "${file}")"
+ else
+ file_info="$(printf '%s(l%d)' "${file}" "${line_nr}")"
+ fi
+
+ warn "${file_info} $(get_msg "${msg_id}")" "${@}"
+
+ return 0
+}
parse_control()
{
- control_file="${1}"
+ local file="${1}"
local field_cb="${2}"
- local req_fields="${3}"
- local opt_fields="${4}"
- shift 4
- local all_fields=
+ local user_data="${3}"
+ shift 3
+ local check_fields=
+ local req_fields=
+ local opt_fields=
local got_fields=
+ local line_nr=
local line=
local name=
local value=
+ local sep=
+
+ check_fields='false'
+ if [ ${#} -eq 2 ]; then
+ req_fields=" ${1} "
+ opt_fields=" ${2} "
+ shift 2
+ check_fields='true'
+ fi
- control_line_nr=0
+ got_fields=' '
- req_fields="$(printf '%s\n' ${req_fields})"
- opt_fields="$(printf '%s\n' ${opt_fields})"
- all_fields="${LF}${req_fields}${LF}${opt_fields}${LF}"
- got_fields="${LF}"
+ line_nr=0
while IFS='' read -r line; do
- control_line_nr=$((${control_line_nr} + 1))
- if [ "x$(echo ${line})" = 'x' ]; then
- parse_control_error 'control_empty_line'
- elif [ "x${line#\#}" != "x${line}" ]; then
- # Comment.
- :
- elif [ "x${line# }" = "x${line}" ]; then
- # "Name: Value" line.
- if [ "x${name}" != 'x' ]; then
- if ! "${field_cb}" "${name}" "${value}"; then
- return 0
+ line_nr=$((${line_nr} + 1))
+ case "${line}" in
+ '')
+ _parse_control_error "${file}" "${line_nr}" \
+ 'control_empty_line'
+ ;;
+ '#'*) # Comment.
+ ;;
+ [!\ ]*':'*) # "Name: Value" line.
+ if [ -n "${name}" ]; then
+ if ! "${field_cb}" "${name}" "${value}"\
+ "${user_data}"; then
+ return 0
+ fi
+ fi
+ IFS=': ' read name value <<-EOF
+ ${line}
+ EOF
+ if [ -z "${name}" ]; then
+ # Badly formatted control field.
+ _parse_control_error "${file}" \
+ "${line_nr}" 'control_bad_nv'
+ continue
fi
- fi
- name="${line%%:*}"
- value="${line#*:}"
- value="${value# }"
- if [ "x${name}" = 'x' -o "x${name}" = "x${line}" ]; then
- parse_control_error 'control_bad_nv'
- continue
- fi
- if [ "x${req_fields}" != 'x' ]; then
- if [ "x${all_fields%${LF}${name}${LF}*}" = \
- "x${all_fields}" ]; then
+ case "${got_fields}" in *" ${name} "*)
+ # Duplicate field.
+ _parse_control_error \
+ "${file}" "${line_nr}" \
+ 'control_duplicate_field' \
+ "${name}"
+ continue
+ esac
+ got_fields="${got_fields}${name} "
+ case "${req_fields}" in *" ${name} "*)
+ # Required field: remove from list.
+ req_fields="${req_fields% ${name} *}$(:\
+ ) ${req_fields#* ${name} }"
+ continue
+ esac
+ if ${check_fields}; then
+ case "${opt_fields}" in *" ${name} "*)
+ # Optional field.
+ continue
+ esac
# Unknown field.
- parse_control_error \
+ _parse_control_error \
+ "${file}" "${line_nr}" \
'control_unknown_field' \
"${name}"
- else
- # Remove field from list of required
- # fields.
- req_fields="$(printf '%s' \
- "${req_fields}" | \
- grep -Fv "${name}")"
fi
- fi
- if [ "x${got_fields%${LF}${name}${LF}*}" != \
- "x${got_fields}" ]; then
- # Duplicate field.
- parse_control_error 'control_duplicate_field' \
- "${name}"
- else
- got_fields="${got_fields}${name}${LF}"
- fi
- else
- # Continuation line.
- if [ "x${name}" = 'x' ]; then
- # Expecting a "Name: Value" line.
- parse_control_error 'control_found_continuation'
- continue
- fi
- value="${value}${LF}${line# }"
- fi
+ ;;
+ ' '*) # Continuation line.
+ if [ -z "${name}" ]; then
+ # Expecting a "Name: Value" line.
+ _parse_control_error "${file}" \
+ "${line_nr}" \
+ 'control_found_continuation'
+ continue
+ fi
+ value="${value}${OB_LF}${line# }"
+ ;;
+ esac
done <<-EOF
- $(cat "${control_file}")
+ $(cat -- "${file}")
EOF
- if [ "x${name}" != 'x' ]; then
- if ! "${field_cb}" "${name}" "${value}"; then
+ if [ -n "${name}" ]; then
+ if ! "${field_cb}" "${name}" "${value}" "${user_data}"; then
return 0
fi
fi
- if [ "x${req_fields}" != 'x' ]; then
- req_fields="$(printf "%s$(get_msg 'list_item_separator')" \
- ${req_fields})"
- parse_control_error 'control_missing_fields' "${req_fields}"
- fi
-
- return 0
-}
-
-parse_control_error()
-{
- local msgid="${1}"
- shift 1
- local file_info=
-
- if [ ${control_line_nr} -eq 0 ]; then
- file_info="$(printf '%20s:' "${control_file}")"
- else
- file_info="$(printf '%20s(l%d):' "${control_file}" \
- "${control_line_nr}")"
+ if ${check_fields}; then
+ case "${req_fields}" in *[!\ ]*)
+ # Missing required control fields.
+ sep="$(get_msg 'list_item_separator')"
+ req_fields="$(printf "%s${sep}" ${req_fields}; \
+ printf 'x')"
+ req_fields="${req_fields%${sep}x}"
+ _parse_control_error "${file}" '0' \
+ 'control_missing_fields' "${req_fields}"
+ esac
fi
- warn "${file_info} $(get_msg "${msgid}")" "${@}"
-
return 0
}