From 0ce81956e20b07daa789416cdb96e771f11da831 Mon Sep 17 00:00:00 2001 From: DrMaxNix Date: Fri, 31 May 2024 21:45:14 +0200 Subject: [PATCH] :tada: initial codebase --- ini.sh | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 ini.sh diff --git a/ini.sh b/ini.sh new file mode 100644 index 0000000..7badc14 --- /dev/null +++ b/ini.sh @@ -0,0 +1,153 @@ +#!/bin/bash +set -euo pipefail + +# ===================================================== +# bash-ini v1.0.0 | (c) 2024 DrMaxNix | www.drmaxnix.de +# ===================================================== + +# +# Parse a ini config file. +# +# @param $1 Path to ini file. +# @param $2 (Optional) prefix for output variable names. +# +ini_parse(){ + ## VALIDATE PATH ## + # get from param + local path="$1" + + # make sure it is readable + if [[ ! -r "$path" ]]; then + ini_log_error "Unable to read config file '$path'" + fi + + + ## VAR PREFIX ## + # get from param or fallback + local var_prefix="${2:-ini}" + + # validate format + if [[ ! "$var_prefix" =~ ^[a-z0-9]+(_[a-z0-9]+)*$ ]]; then + ini_log_error "Invalid variable prefix '$var_prefix'" + fi + + # build global var names + local -n out_section_list="${var_prefix}" + + + local line + local section_name + while IFS= read -r line; do + ## TRIM WHITESPACES ## + line=$(echo "$line" | sed -E "s/^\s*//g" | sed -E "s/\s*$//g") + + + ## IGNORE LINES ## + # empty lines + if [[ "$line" == "" ]]; then + # ignore + continue; fi + + # comments + if [[ "$line" =~ ^# || "$line" =~ ^\; ]]; then + # ignore + continue; fi + + + ## MATCH SECTION HEADER ## + if [[ "$line" =~ ^\[.*\]$ ]]; then + # get section name + section_name=$(echo "$line" | sed -E "s/^\[(.*)\]$/\1/g") + + # trim whitespaces + section_name=$(echo "$section_name" | sed -E "s/^\s*//g" | sed -E "s/\s*$//g") + + # validate format + if [[ ! "$section_name" =~ ^[A-Za-z0-9]+([\._-][A-Za-z0-9]+)*$ ]]; then + ini_log_error "Invalid section name '$section_name'" + fi + + # rewrite section name + section_name=$(echo "$section_name" | tr 'A-Z' 'a-z' | tr '-' '_' | tr '.' '_') + + # maybe add to section list + local found="no" + for s in ${out_section_list[*]:-}; do + if [[ "$s" == "$section_name" ]]; then found="yes"; break; fi + done + if [[ "$found" != "yes" ]]; then + out_section_list=(${out_section_list[@]:-""} "$section_name") + fi + + # declare section map + section_map_varname="${var_prefix}__${section_name}" + declare -g -A ${section_map_varname} + continue; fi + + + ## MATCH ASSIGNMENT ## + if [[ "$line" =~ ^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[[:space:]\t]*=[[:space:]\t]*(.*)$ ]]; then + # maybe enter root section + if [[ ! "${section_name:+x}" ]]; then + section_name="_root" + out_section_list=(${out_section_list[@]:-""} "$section_name") + section_map_varname="${var_prefix}__${section_name}" + declare -g -A ${section_map_varname} + fi + + # get key and value + local key=$(echo "$line" | sed -E "s/^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[ \t]*=[ \t]*(.*)$/\1/g") + local value=$(echo "$line" | sed -E "s/^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[ \t]*=[ \t]*(.*)$/\3/g") + + # maybe trim quote marks + if [[ "$value" =~ ^\".*\"$ ]]; then + value=$(echo "$value" | sed -E "s/^\"(.*)\"$/\1/g") + elif [[ "$value" =~ ^\'.*\'$ ]]; then + value=$(echo "$value" | sed -E "s/^'(.*)'$/\1/g") + fi + + # maybe add key to key list + local -n out_section_key_list="${var_prefix}__${section_name}__key_list" + local found="no" + for k in ${out_section_key_list[*]:-}; do + if [[ "$k" == "$key" ]]; then found="yes"; break; fi + done + if [[ "$found" != "yes" ]]; then + out_section_key_list=(${out_section_key_list[@]:-""} "$key") + fi + + # set value in section map + local -n out_section_map="${var_prefix}__${section_name}" + out_section_map[$key]="$value" + continue; fi + + + ## UNKNOWN ## + ini_log_error "Unknown config string '$line'" + done < "$path" +} + + + +# +# HELPER: try calling external logger, else use fallback. +# +# @param $1 Message to print. +# +ini_log_debug(){ + if [[ "$(type -t log_debug)" == "function" ]]; then log_debug "$1"; return; fi + echo "[DEBUG] $1" 1>&2 +} +ini_log_info(){ + if [[ "$(type -t log_info)" == "function" ]]; then log_info "$1"; return; fi + echo "[INFO ] $1" 1>&2 +} +ini_log_warn(){ + if [[ "$(type -t log_warn)" == "function" ]]; then log_warn "$1"; return; fi + echo "[WARN ] $1" 1>&2 +} +ini_log_error(){ + if [[ "$(type -t log_error)" == "function" ]]; then log_error "$1"; return; fi + echo "[ERROR] $1" 1>&2 + exit 1 +}