# Functions for parsing control files. # # Copyright (C) 2012, 2013 Patrick "P. J." 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 1 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 check_fields= local req_fields= local line_nr= local in_paragraph= local line= local para_req_fields= local got_fields= local name= local value= check_fields='false' if [ ${#} -eq 1 ]; then req_fields="${1}" shift 1 check_fields='true' 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 if [ -n "${para_req_fields}" ]; then para_req_fields="$(printf \ "%s$(get_msg \ 'list_item_separator')"\ ${para_req_fields})" _parse_control_error \ "${file}" "${line_nr}" \ 'control_missing_fields'\ "${para_req_fields}" fi fi para_req_fields="$(printf '%s\n' ${req_fields})" got_fields="${LF}" 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 if [ -n "${para_req_fields}" ]; then # Remove field from list of required # fields. para_req_fields="$(printf '%s' \ "${para_req_fields}" | \ grep -Fv "${name}")" fi if [ "x${got_fields%${LF}${name}${LF}*}" != \ "x${got_fields}" ]; then # Duplicate field. _parse_control_error \ "${file}" "${line_nr}" \ 'control_duplicate_field' \ "${name}" else got_fields="${got_fields}${name}${LF}" fi ;; ' '*) # 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. if [ -n "${name}" ]; then if ! "${field_cb}" "${name}" "${value}"; then return 0 fi fi return 0 }