# Functions for parsing control files. # # Copyright (C) 2012, 2013, 2019 Patrick McDermott # # This file is part of the ProteanOS Development Kit. # # The ProteanOS Development Kit is free software: you can redistribute # it and/or modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # The ProteanOS Development Kit is distributed in the hope that it # will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the ProteanOS Development Kit. If not, see # . _parse_control_error() { local file="${1}" local line_nr="${2}" local msg_id="${3}" shift 3 local file_info= if [ ${line_nr} -eq 0 ]; then file_info="$(printf '%s' "${file}")" else file_info="$(printf '%s:%d' "${file}" "${line_nr}")" fi warn "${file_info}: $(get_msg "${msg_id}")" "${@}" return 0 } parse_control() { local file="${1}" local field_cb="${2}" local paragraph_cb="${3}" shift 3 local req_fields= local line_nr= local in_paragraph= local line= local para_req_fields= local got_fields= local name= local value= if [ ${#} -eq 1 ]; then req_fields=" ${1} " shift 1 fi line_nr=0 in_paragraph='false' while IFS='' read -r line; do line_nr=$((${line_nr} + 1)) case "${line}" in '') # Paragraph end. if ${in_paragraph}; then # The first line is blank to consolidate # initialization code (see heredocument # below). in_paragraph='false' if [ -n "${name}" ]; then if ! "${field_cb}" "${name}" \ "${value}"; then return 0 fi fi if ! "${paragraph_cb}"; then return 0 fi case "${para_req_fields}" in *[!\ ]*) # Missing required control # fields. sep="$(get_msg \ 'list_item_separator')" para_req_fields="$(printf \ "%s${sep}" \ ${para_req_fields}; \ printf 'x')" para_req_fields="$(: \ )${para_req_fields%$(: \ )${sep}x}" _parse_control_error \ "${file}" "${line_nr}" \ 'control_missing_fields'\ "${para_req_fields}" esac fi para_req_fields="${req_fields}" got_fields=' ' name='' value='' ;; '#'*) # Comment. in_paragraph='true' ;; [!\ ]*':'*) # "Name: Value" line. in_paragraph='true' if [ -n "${name}" ]; then if ! "${field_cb}" "${name}" "${value}" then return 0 fi fi IFS=': ' read name value <<-EOF ${line} EOF if [ -z "${name}" ]; then _parse_control_error "${file}" \ "${line_nr}" 'control_bad_nv' continue fi case "${got_fields}" in *" ${name} "*) # Duplicate field. _ob_parse_control_error \ "${file}" "${line_nr}" \ 'control_duplicate_field' \ "${name}" continue esac got_fields="${got_fields}${name} " case "${para_req_fields}" in *" ${name} "*) # Required field: remove from list. para_req_fields="$(:\ )${para_req_fields% ${name} *}$( :\ ) ${para_req_fields#* ${name} }" continue esac ;; ' '*) # Continuation line. in_paragraph='true' if [ -z "${name}" ]; then # Expecting a "Name: Value" line. _parse_control_error "${file}" \ "${line_nr}" \ 'control_found_continuation' continue fi value="${value}${LF}${line# }" ;; esac done <<-EOF $(cat -- "${file}") EOF # The first blank line above triggers the paragraph end code to # consolidate initialization code. # The second blank line above is needed because the command substitution # removes any trailing newlines. return 0 }