Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
This software is provided under the BSD license. The text of this license
is provided below:

--------------------------------------------------------------------------

Copyright (C) 2009 Kevin Porter / Advanced Web Construction Ltd
Copyright (C) 2010-2015 Ruediger Meier
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3. Neither the name of the author nor the names of any contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10 changes: 8 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ COPYRIGHT

Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd
(http://coding.tinternet.info / http://webutils.co.uk)
Copyright (c) 2010-2012 Ruediger Meier <sweet_f_a@gmx.de>
Copyright (c) 2010-2015 Ruediger Meier <sweet_f_a@gmx.de>
(https://github.com/rudimeier/)

License: BSD-3-Clause, see LICENSE file




Expand Down Expand Up @@ -44,6 +46,8 @@ SECTION and VARNAME are the section name and variable name respectively.

Additionally you can get a list of all these variable names:
PREFIX__ALL_VARS
and get a list of sections:
PREFIX__ALL_SECTIONS
and the number of sections:
PREFIX__NUMSECTIONS

Expand Down Expand Up @@ -94,7 +98,9 @@ Whether to interpret special unquoted string values 'yes', 'no', 'true',
'false', 'on', 'off' as booleans.
Default: 1


[--allow_no_value | -nv] [0|1]
Whether to allow keys without values in the ini file.
Default: 0


INI FILE FORMAT
Expand Down
148 changes: 108 additions & 40 deletions read_ini.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#
# Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd
# (http://coding.tinternet.info, http://webutils.co.uk)
# Copyright (c) 2010-2012 Ruediger Meier <sweet_f_a@gmx.de>
# Copyright (c) 2010-2015 Ruediger Meier <sweet_f_a@gmx.de>
# (https://github.com/rudimeier/)
#
# License: BSD-3-Clause, see LICENSE file
#
# Simple INI file parser.
#
# See README for usage.
Expand Down Expand Up @@ -60,19 +62,43 @@ function read_ini()

# Set defaults
local BOOLEANS=1
local NV=0
local VARNAME_PREFIX=INI
local CLEAN_ENV=0
local INDENT_LEVEL=0
local CURRENT_INDENT_LEVEL=0
local LONGEST_LINE=0

# Regular expression so skip blank lines and commented out lines
local RE_SKIP='^(\s*$)|^\s*(#|;)'

# Regular expression to fine leading spaces
RE_SPACES='^(\ +)'

# Regular expression for tracking down section markers
RE_SEC='\[([^]]+)\]'

# Regular expression for value lines
RE=''
RE_REG='^(.*?)\s*(=|:)\s*(.+)$'

# Regular expression to use if optional values are allowed
RE_OPT='^(.*?)\s*(=|:)\s*(.*)$|(.*?)\s*$'
#RE_OPT='^(.*?)\s*(?:(=|:)\s*(.*))?$'

# {{{ START Options

# Available options:
# --boolean Whether to recognise special boolean values: ie for 'yes', 'true'
# and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted
# values will be left as strings
# Default: on
# --boolean Whether to recognise special boolean values: ie for 'yes', 'true'
# and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted
# values will be left as strings
# Default: on
#
# --prefix=STRING String to begin all returned variables with (followed by '__').
# Default: INI
# --allow_no_value Whether keys without values are allowed in the ini file: ie. 'key' or 'key='
# Default: off
#
# --prefix=STRING String to begin all returned variables with (followed by '__').
# Default: INI
#
# First non-option arg is filename, second is section name

Expand All @@ -90,6 +116,10 @@ function read_ini()
BOOLEANS=$1
;;

--allow_no_value | -nv )
NV=1
;;

--prefix | -p )
shift
VARNAME_PREFIX=$1
Expand All @@ -113,7 +143,7 @@ function read_ini()
done

if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ] ;then
echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE"\
echo -e "Usage: read_ini [-c] [-b 0| -b 1] [-nv] [-p PREFIX] FILE"\
"[SECTION]\n or read_ini -c [-p PREFIX]" >&2
cleanup_bash
return 1
Expand All @@ -124,16 +154,21 @@ function read_ini()
return 1
fi

local INI_ALL_VARNAME="${VARNAME_PREFIX}__ALL_VARS"
local INI_ALL_SECTION="${VARNAME_PREFIX}__ALL_SECTIONS"
local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}__NUMSECTIONS"
if [ "${CLEAN_ENV}" = 1 ] ;then
# TODO How to clear the whole array without unset it
for i in "${!INI[@]}" ;do
unset ${VARNAME_PREFIX}['$i']
done
eval unset "\$${INI_ALL_VARNAME}"
fi

# TODO How to declare -A ${VARNAME_PREFIX} non local? Or we have to
# check for a global declared one
unset ${INI_ALL_VARNAME}
unset ${INI_ALL_SECTION}
unset ${INI_NUMSECTIONS_VARNAME}

if [ -z "$INI_FILE" ] ;then
Expand All @@ -152,6 +187,15 @@ function read_ini()
BOOLEANS=1
fi

if [ "$NV" == "0" ]
then
RE=$RE_REG
else
RE=$RE_OPT
fi

# Get longest line in the file - used to reset indentation level
LONGEST_LINE=$(wc -L < "$INI_FILE")

# }}} END Options

Expand All @@ -160,43 +204,48 @@ function read_ini()
local LINE_NUM=0
local SECTIONS_NUM=0
local SECTION=""

# IFS is used in "read" and we want to switch it within the loop
local IFS=$' \t\n'
local IFS_OLD="${IFS}"


# we need some optional shell behavior (shopt) but want to restore
# current settings before returning
local SWITCH_SHOPT=""
pollute_bash

while read -r line || [ -n "$line" ]
while IFS= read -r line || [ -n "$line" ]
do
#echo line = "$line"

((LINE_NUM++))

# Skip blank lines and comments
if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ]
if [[ "${line}" =~ $RE_SKIP ]]
then
# Empty line marks end of value
INDENT_LEVEL="$LONGEST_LINE"
continue
fi

# Check if multi-line value
if [[ "${line}" =~ $RE_SPACES ]]
then
LEADING_SPACES="${BASH_REMATCH[1]}"
CUR_INDENT_LEVEL=${#LEADING_SPACES}
else
CUR_INDENT_LEVEL=0
fi

# Section marker?
if [[ "${line}" =~ ^\[.*\]$ ]]
if [[ "${line}" =~ $RE_SEC ]]
then
# Set SECTION var to name of section (strip [ and ] from section marker)
SECTION="${line#[}"
SECTION="${SECTION%]}"
read SECTION <<<"${SECTION}"
# Set SECTION var to name of section
SECTION="${BASH_REMATCH[1]}"
if ! [[ "${line}" =~ ^[[:print:]]+$ ]] ;then
echo "Error: Invalid section:" >&2
echo " ${LINE_NUM}: '$line'" >&2
cleanup_bash
return 1
fi
eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\""
((SECTIONS_NUM++))

continue
fi

Expand All @@ -209,26 +258,46 @@ function read_ini()
fi
fi

# Valid var/value line? (check for variable name and then '=')
if ! [[ "${line}" =~ ^[[:print:]]+[[:space:]]*= ]]
# Valid var/value line? (check for variable name and then '=' or ':')
LINE_IS_VALID=1

if [[ "${line}" =~ $RE ]]
then
if [ $CUR_INDENT_LEVEL -gt $INDENT_LEVEL ]
then
VAL="$VAL${line}"
else
VAR="${BASH_REMATCH[1]}"
VAL="${BASH_REMATCH[3]}"
INDENT_LEVEL=$CUR_INDENT_LEVEL
fi
elif [ $CUR_INDENT_LEVEL -gt $INDENT_LEVEL ]
then
line="${line##+([[:space:]])}"
VAL="$VAL\\n${line}"
else
LINE_IS_VALID=0
fi

if [ "$VAL" = "" ] && [ "$NV" = 0 ]
then
echo "Error: Invalid line:" >&2
LINE_IS_VALID=0
fi

if [ "$LINE_IS_VALID" = 0 ]
then
echo "Error: Invalid line:" >&2
echo " ${LINE_NUM}: '$line'" >&2
cleanup_bash
return 1
fi

fi

# split line at "=" sign
IFS="="
read -r VAR VAL <<< "${line}"
IFS="${IFS_OLD}"

# delete spaces around the equal sign (using extglob)
VAR="${VAR##+([[:space:]])}"
VAR="${VAR%%+([[:space:]])}"
VAL="${VAL##+([[:space:]])}"
VAR=$(echo $VAR)

VAL="${VAL##+([[:space:]])}"
VAL="${VAL%%+([[:space:]])}"

# Construct variable name:
# ${VARNAME_PREFIX}__$SECTION__$VAR
Expand All @@ -241,17 +310,16 @@ function read_ini()
else
VARNAME=${SECTION}__${VAR//./_}
fi
eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\""

if [[ "${VAL}" =~ ^\".*\"$ ]]
if [[ "${VAL}" =~ (^\s*\")(.*)(\"\s*) ]]
then
# remove existing double quotes
VAL="${VAL##\"}"
VAL="${VAL%%\"}"
elif [[ "${VAL}" =~ ^\'.*\'$ ]]
VAL="${BASH_REMATCH[2]}"
elif [[ "${VAL}" =~ (^\s*\')(.*)(\'\s*) ]]
then
# remove existing single quotes
VAL="${VAL##\'}"
VAL="${VAL%%\'}"
VAL="${BASH_REMATCH[2]}"
elif [ "$BOOLEANS" = 1 ]
then
# Value is not enclosed in quotes
Expand All @@ -269,7 +337,7 @@ function read_ini()
;;
esac
fi

# echo "pair: '${VARNAME}' = '${VAL}'"
eval ${VARNAME_PREFIX}$'[${VARNAME}]=${VAL}'
# eval $'echo "array: \'${VARNAME}\' = \'${'${VARNAME_PREFIX}$'[${VARNAME}]}\'"'
Expand Down
4 changes: 2 additions & 2 deletions test/test1.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ var4="VAR 4"
; not become part of the value
var5 = " VAR 5 "

; var6 - value in double quotes; value's leading and trailing whitespace should
; be preserved; leading and trailing whitespace before/after double quotes should
; var6 - value in single quotes; value's leading and trailing whitespace should
; be preserved; leading and trailing whitespace before/after single quotes should
; not become part of the value
var6 = ' VAR 6 '

Expand Down
41 changes: 41 additions & 0 deletions test/test10.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

; Testing Python configuration file format
; see https://docs.python.org/3/library/configparser.html

[Simple Values]
key=value
spaces in keys=allowed
spaces in values=allowed as well
spaces around the delimiter = obviously
you can also use : to delimit keys from values

[All Values Are Strings]
values like this: 1000000
or this: 3.14159265359
are they treated as numbers? : no
integers, floats and booleans are held as: strings
can use the API to get converted values directly: true

[Multiline Values]
chorus: I'm a lumberjack, and I'm okay
I sleep all night and I work all day

[You can use comments]
# like this
; or this

# By default only in an empty line.
# Inline comments can be harmful because they prevent users
# from using the delimiting characters as parts of values.
# That being said, this can be customized.

[Sections Can Be Indented]
can_values_be_as_well = True
does_that_mean_anything_special = False
purpose = formatting for readability
multiline_values = are
handled just fine as
long as they are indented
deeper than the first line
of a value
# Did I mention we can indent comments, too?
Loading