Skip to content

Commit

Permalink
initial commit awc_bash_ini_parser-0.3.tgz
Browse files Browse the repository at this point in the history
  • Loading branch information
rudimeier committed Dec 8, 2010
0 parents commit bce351a
Show file tree
Hide file tree
Showing 25 changed files with 734 additions and 0 deletions.
101 changes: 101 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

COPYRIGHT
=========

Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd
(http://coding.tinternet.info / http://webutils.co.uk)


USAGE
=====

You must source the bash file into your script:

> . read_ini.sh

and then use the read_ini function, defined as:

> read_ini INI_FILE [SECTION] [[--prefix|-p] PREFIX] [[--booleans|b] [0|1]]

If SECTION is supplied, then only the specified section of the file will
be processed.

After running the read_ini function, variables corresponding to the ini
file entries will be available to you. Naming convention for variable
names is:

PREFIX__SECTION__VARNAME

PREFIX is 'INI' by default (but can be changed with the --prefix option),
SECTION and VARNAME are the section name and variable name respectively.
For example, to read and output the variables of this ini file:

-- START test1.ini file

var1="VAR 1"
var2 = VAR 2

[section1]
var1="section1 VAR 1"
var2= section1 VAR 2


-- END test1.ini file

you could do this:

-- START bash script

. read_ini.sh

read_ini test1.ini

echo "var1 = ${INI__var1}"
echo "var2 = ${INI__var2}"
echo "section1 var1 = ${INI__section1__var1}"
echo "section1 var2 = ${INI__section1__var2}"

-- END bash script


OPTIONS
=======

[--prefix | -p] PREFIX
String to prepend to generated variable names (automatically followed by '__').
Default: INI

[--booleans | -b] [0|1]
Whether to interpret special unquoted string values 'yes', 'no', 'true',
'false', 'on', 'off' as booleans.
Default: 1


INI FILE FORMAT
===============

- Variables are stored as name/value pairs, eg:
var=value

- Leading and trailing whitespace of the name and the value is discarded.

- Use double or single quotes to get whitespace in the values

- Section names in square brackets, eg:
[section1]
var1 = value

- Variable names can be re-used between sections (or out of section), eg:
var1=value
[section1]
var1=value
[section3]
var1=value

- Dots are converted to underscores in all variable names.

- Special boolean values: unquoted strings 'yes', 'true' and 'on' are interpreted
as 1; 'no', 'false' and 'off' are interpreted as 0



7 changes: 7 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


- Tabs/newlines to be preserved

- [] notation for arrays (like PHP's parse_ini_file())


237 changes: 237 additions & 0 deletions read_ini.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#
# Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd
# (http://coding.tinternet.info, http://webutils.co.uk)
#
# Simple INI file parser.
#
# See README for usage.
#
#


function read_ini()
{

local INI_FILE=""
local INI_SECTION=""

# {{{ START Deal with command line args

# Set defaults
local BOOLEANS=1
local VARNAME_PREFIX=INI

# {{{ 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
#
# --prefix=STRING String to begin all returned variables with (followed by '__').
# Default: INI
#
# First non-option arg is filename, second is section name

while [ $# -gt 0 ]
do

case $1 in

--booleans | -b )
shift
BOOLEANS=$1
;;

--prefix | -p )
shift
VARNAME_PREFIX=$1
;;

* )
if [ -z "$INI_FILE" ]
then
INI_FILE=$1
else
if [ -z "$INI_SECTION" ]
then
INI_SECTION=$1
fi
fi
;;

esac

shift
done

if [ -z "$INI_FILE" ]
then
echo "Usage: read_ini FILE [SECTION]" >&2
return 1
fi

if [ ! -f "$INI_FILE" ]
then
echo "Error: ini file '${INI_FILE}' doesn't exist" >&2
return 1
fi

# Be strict with the prefix, since it's going to be run through eval
local PREFIX_BANNED_CHARS=$(echo "$VARNAME_PREFIX" | sed 's/[a-z0-9_]//ig')

if [ -n "$PREFIX_BANNED_CHARS" ]
then
echo "Invalid characters ('${PREFIX_BANNED_CHARS}') in variable name prefix ('${VARNAME_PREFIX}')" >&2
return 1
fi

# Prefix can't start with a number
local FIRSTCHAR=${VARNAME_PREFIX:0:1}
local BEGINS_WITH_NUMBER=""
case $FIRSTCHAR in
0|1|2|3|4|5|6|7|8|9)
echo "Invalid variable name prefix - must not begin with a number" >&2
return 1
;;
esac

# Sanitise BOOLEANS - interpret "0" as 0, anything else as 1
if [ "$BOOLEANS" != "0" ]
then
BOOLEANS=1
fi


# }}} END Options

# }}} END Deal with command line args

local LINE_NUM=0
local SECTION=""
while read line
do

#echo line = "$line"

((LINE_NUM++))

# Skip blank lines and comments
if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ]
then
continue
fi

# Section marker?
local line_rev=$(echo "$line" | rev)
if [ "${line:0:1}" = "[" ] && [ "${line_rev:0:1}" = "]" ]
then

# Set SECTION var to name of section (strip [ and ] from section marker)
SECTION="${line#[}"
SECTION="${SECTION%]}"

continue
fi

# Are we getting only a specific section? And are we currently in it?
if [ ! -z "$INI_SECTION" ]
then
if [ "$SECTION" != "$INI_SECTION" ]
then
continue
fi
fi

# Valid var/value line? (check for variable name and then '=')
local VAR_VAL=$(echo "$line" | awk 'BEGIN { FS="=" } /^[a-zA-Z0-9._-]+[[:space:]]*=/ { print $1,"__INI__PARSER__DELIMITER__",$2; }')
#echo "VAR_VAL = *$VAR_VAL*"

if [ -z "$VAR_VAL" ]
then
echo "Error: Invalid line:" >&2
echo " ${LINE_NUM}: $line" >&2
return 1
fi

local VAR=$(echo "$VAR_VAL" | awk -F__INI__PARSER__DELIMITER__ '{print $1}')
local VAL=$(echo "$VAR_VAL" | awk -F__INI__PARSER__DELIMITER__ '{sub(/^[[:space:]]+/,"",$2); print $2;}')
VAR=$(echo $VAR)
#echo VAL = $VAL


# Construct variable name:
# ${VARNAME_PREFIX}__$SECTION__$VAR
# Or if not in a section:
# ${VARNAME_PREFIX}__$VAR
# In both cases, full stops ('.') are replaced with underscores ('_')
if [ -z "$SECTION" ]
then
VARNAME=${VARNAME_PREFIX}__${VAR//./_}
else
VARNAME=${VARNAME_PREFIX}__${SECTION}__${VAR//./_}
fi

# Surround VAL with quotes if it isn't already
local FIRSTCHAR=${VAL:0:1}
local LASTCHAR=${VAL:$LEN-1:1}
local DOUBLEQUOTES=""
local SINGLEQUOTES=""

if [ "$FIRSTCHAR" = '"' -a "$LASTCHAR" = '"' ]
then
DOUBLEQUOTES=1
fi

if [ "$FIRSTCHAR" = "'" -a "$LASTCHAR" = "'" ]
then
SINGLEQUOTES=1
fi

if [ -z "$SINGLEQUOTES" -a -z "$DOUBLEQUOTES" ]
then
# Value is not enclosed in quotes

# If we have booleans processing switched on, check for special boolean
# values and convert
if [ "$BOOLEANS" == 1 ]
then

# Check length of string first. Since we're going to use tr command to convert
# the string to lowercase, it'll be more efficient if we check string length
# first. If value is more than 5 chars then it can't possibly be one of the
# special boolean values
if [ "${#VAL}" -le 5 ]
then

# Convert to lower case
local VAL_LOWER=$(echo "$VAL" | tr '[:upper:]' '[:lower:]')

case "$VAL_LOWER" in
yes | true | on )
VAL=1
;;
no | false | off )
VAL=0
;;
esac
fi

fi

# We'll enclose the value in double quotes now, so we must escape any
# double quotes that may be in the value first
VAL=$(echo "$VAL" | awk '{sub(/"/,"\\\"",$0); print $0;}')
VAL="\"$VAL\""
fi

# Replace $ and ` to prevent code running inside eval
VAL=${VAL//\`/\\\`}
VAL=${VAL//\$/\\\$}
#declare -x $VARNAME="$VAL"
eval "$VARNAME=$VAL"
done < <(cat $INI_FILE)
}


28 changes: 28 additions & 0 deletions test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

DIR=$(dirname $0)
cd $DIR

TESTS=$(ls test*.sh | grep -v test.sh | sed 's/\.sh$//')

for test in $TESTS
do

bash $test.sh &> $test.out
# bash $test.sh >$test.out 2>$test.err

# Fail and bail out if test didn't pass
PASSED=$(diff $test.out $test.out.correct 2>&1)
if [ ! -z "$PASSED" ]
then
echo "Test $test failed. Output is in $DIR/$test.out"
exit 1
else
rm -rf $test.out
fi

done


echo "All tests passed"


Loading

0 comments on commit bce351a

Please sign in to comment.