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
7 changes: 1 addition & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
Makefile
autom4te.cache
bashlib
config.log
config.status
configure
bashlib-*.tar.gz
19 changes: 13 additions & 6 deletions INSTALL
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
bashlib 0.5
bashlib 4

Installation of bashlib is relatively straightforward:
bashlib is a bash library: there is nothing to configure and nothing
to build, the checked-out file is the finished library (it calls no
external tools). To run the test suite:

# make check

To install (the default prefix is /usr/local):

# gunzip -c bashlib-0.5.tar.gz | tar xf -
# cd bashlib-0.5
# ./configure
# make install
# make install PREFIX=/usr # or wherever

Packagers can use the usual DESTDIR redirection:

# make install DESTDIR=$RPM_BUILD_ROOT

Using bashlib in your CGI scripts is as simple as beginning your
scripts with:
Expand Down Expand Up @@ -38,7 +46,6 @@ needs to be done. Patches and feature requests are accepted.
TODO

* Better documentation, probably a man page
* Autoconf-based install script
* More functions
* Functions to generate HTML
* Functions to set cookies
Expand Down
46 changes: 19 additions & 27 deletions Makefile.in → Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# Makefile for bashlib
# ----------------------------------------------------------------------
# $Id$
# @configure_input@
# ----------------------------------------------------------------------
# bashlib
# bashlib
# Copyright (C) 2002-2005 darren chamberlain <dlc@sevenroot.org>
#
# This program is free software; you can redistribute it and/or modify
Expand All @@ -20,35 +17,30 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
# ----------------------------------------------------------------------
PREFIX = @prefix@
VERSION = @bashlib_version@
# There is nothing to configure or build: bashlib calls no external
# tools, so the checked-out file is the finished library.
PREFIX ?= /usr/local
DESTDIR ?=
VERSION = 4

all:
./configure
@echo "nothing to build; try 'make check' or 'make install'"

install:
@INSTALL@ bashlib $(PREFIX)/bin
mkdir -p $(DESTDIR)$(PREFIX)/bin
install -m 755 bashlib $(DESTDIR)$(PREFIX)/bin

check:
./run_tests.sh

dist:
@MKDIR@ bashlib-$(VERSION)
@CP@ configure bashlib-$(VERSION)/
@CP@ bashlib.in bashlib-$(VERSION)/
@CP@ Makefile.in bashlib-$(VERSION)/
@CP@ INSTALL bashlib-$(VERSION)/
@CP@ COPYING bashlib-$(VERSION)/
mkdir bashlib-$(VERSION)
cp bashlib Makefile INSTALL COPYING run_tests.sh bashlib-$(VERSION)/
cp -r examples bashlib-$(VERSION)/
cd bashlib-$(VERSION); ln -s INSTALL README
@TAR@ cf bashlib-$(VERSION).tar bashlib-$(VERSION)
@GZIP@ --best bashlib-$(VERSION).tar
@RM@ -rf bashlib-$(VERSION)
tar cf bashlib-$(VERSION).tar bashlib-$(VERSION)
gzip --best bashlib-$(VERSION).tar
rm -rf bashlib-$(VERSION)

clean:
@RM@ -f Makefile bashlib config.cache config.log config.status
@RM@ -fr ./autom4te.cache/

cvs-clean:
$(MAKE) clean
@RM@ -f configure
@RM@ -f bashlib-$(VERSION).tar.gz

distclean:
$(MAKE) cvs-clean
rm -f bashlib-$(VERSION).tar bashlib-$(VERSION).tar.gz
287 changes: 287 additions & 0 deletions bashlib
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
#!/bin/bash

# Author: darren chamberlain <dlc@sevenroot.org>
# Co-Author: Paul Bournival <paulb-ns@cajun.nu>
# Co-Author: Mikhail Novosyolov <mikhailnov@dumalogiya.ru>
#

# bashlib is used by sourcing it at the beginning of scripts that
# needs its functionality (by using the . or source commands).
#
# The library targets bash: it relies on bash-only features such as
# ${!var} indirection, printf -v, ${!PREFIX@} listings, ${var//pat/rep}
# substitution and read -d. All hot paths are fork-free (builtins only):
# param()/cookie() used to spawn env|grep|sed|cut on every call, and the
# URL decoding used to fork once per %XX escape.

#
# Set version number
# Must be an integer because bash cannot compare float numbers
#
VERSION="4"

# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# Initialization stuff begins here. These things run immediately, and
# do the parameter/cookie parsing.
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

# Characters removed by safe_param(), as one glob character class.
# The class is built by concatenation because a literal ' cannot
# appear inside a '...' string: '[$`<>"%;)(&+' + "'" + ']'.
SAFE_STRIP='[$`<>"%;)(&+'"'"']'

# capture stdin for POST methods. POST requests don't always come in
# with a newline attached, so read everything up to EOF (the empty -d
# delimiter means NUL, which never occurs in CGI input). A plain
# $(cat) would fork; read is a builtin.
STDIN=
IFS= read -r -d '' STDIN || true
if [ -n "${STDIN}" ]; then
QUERY_STRING="${STDIN}&${QUERY_STRING}"
fi

# Handle GET and POST requests... (the QUERY_STRING will be set)
if [ -n "${QUERY_STRING}" ]; then
# name=value params, separated by either '&' or ';'
case ${QUERY_STRING} in
*=*)
# '&' and ';' become spaces, so the unquoted expansion below splits
# QUERY_STRING into one word per parameter:
# "a=1&b=2;c=3" -> "a=1 b=2 c=3" -> words "a=1" "b=2" "c=3"
for Q in ${QUERY_STRING//[;&]/ } ; do
#
# Clear our local variables
#
unset name
value=

#
# Decode the name of the key so that it can never break the eval
# below: turn %XX into \xHH for printf, decode '+' as a space,
# then strip '.', '-', '$' and '`'.
#
# cut at the first '=': "user%2Ename=admin" -> "user%2Ename"
name=${Q%%=*}
# every % becomes \x: "user%2Ename" -> "user\x2Ename"
name=${name//%/\\x}
# '+' is an encoded space: "a+b+c" -> "a b c"
name=${name//+/ }
# delete '.' and '-' (literal ones only; encoded %2E/%2D survive
# here and decode back below): "user.name-x" -> "usernamex"
name=${name//[.-]/}
# decode \xHH hex: "user\x2Enamex" -> "user.namex"
printf -v name '%b' "${name}"
# delete every '$'
name=${name//$/}
# delete every '`'
name=${name//\`/}

#
# Decode the value: turn %XX into \xHH and let a single printf
# do the whole job. printf reads at most two hex digits per
# \xHH, so no separators between escapes are needed.
#
# cut at the first '=': "q=hello%21" -> "hello%21"
value=${Q#*=}
# decode %XX: "hello%21" -> "hello!"
printf -v value '%b' "${value//%/\\x}"

eval "export FORM_${name}='${value}'"
done
;;
*) # keywords: foo.cgi?a+b+c
# "alpha+beta+gamma" -> "alpha beta gamma"
eval "export KEYWORDS='${QUERY_STRING//+/ }'"
;;
esac
fi

#
# this section works identically to the query string parsing code,
# with the (obvious) exception that variables are stuck into the
# environment with the prefix COOKIE_ rather than FORM_. This is to
# help distinguish them from the other variables that get set
# automatically.
#
if [ -n "${HTTP_COOKIE}" ]; then
for Q in ${HTTP_COOKIE}; do
#
# Clear our local variables
#
name=
value=

#
# drop one trailing ';': "session=abc123;" -> "session=abc123"
#
Q=${Q%;}

#
# Decode the name of the key; see the parameter section above.
#
# cut at the first '=': "session=abc123" -> "session"
name=${Q%%=*}
# every % becomes \x: "a%73b" -> "a\x73b"
name=${name//%/\\x}
# '+' is an encoded space: "a+b" -> "a b"
name=${name//+/ }
# delete '.' and '-': "user.name" -> "username"
name=${name//[.-]/}
# decode \xHH hex: "a\x73b" -> "asb"
printf -v name '%b' "${name}"

#
# Decode the cookie value; see the parameter section above.
#
# cut at the first '=': "q=a%20b" -> "a%20b"
value=${Q#*=}
# decode %XX: "a%20b" -> "a b"
printf -v value '%b' "${value//%/\\x}"

#
# Export COOKIE_${name} into the environment
#
eval "export COOKIE_${name}='${value}'"
done
fi

# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# functions and all that groovy stuff
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#
# Shameless plug, advertises verion.
version() {
echo "bashlib, version ${VERSION}"
}

version_html() {
echo -n "<a href=\"http://sevenroot.org/dlc/2000/12/bashlib\">bashlib</a>,"
echo "version ${VERSION}"
}

#
# Internal helper: store the raw stored value of parameter $1 in REPLY.
# Anything that is not a valid variable name yields an empty value.
#
_param_value() {
# "foo" and "FORM_foo" both give "FORM_foo"
local name="FORM_${1#FORM_}"
case ${name} in
# empty name or not a valid variable name -> empty value
FORM_|FORM_*[!A-Za-z0-9_]*) REPLY= ;;
# ${!name} is the value of the variable whose name is in $name
*) REPLY="${!name}" ;;
esac
}

#
# Parameter function.
# * When called with no arguments, returns a list of parameters that
# were passed in.
# * When called with one argument, returns the value of that parameter
# (if any)
# * When called with more than one argument, assumes that the first is a
# paramter name and the rest are values to be assigned to a paramter of
# that name.
#
param() {
local name value
if [ $# -eq 1 ]; then
_param_value "$1"
# '+' is an encoded space: "a+b" -> "a b"
value="${REPLY//+/ }"
elif [ $# -gt 1 ]; then
name=$1
shift
eval "export 'FORM_${name}=$*'"
return
else
# ${!FORM_@} expands to every existing variable named FORM_*
for name in ${!FORM_@}; do
# "FORM_foo" -> "foo"
echo "${name#FORM_}"
done
return
fi
echo "${value}"
unset name
unset value
}

# shell invocation and X-site scripting prevention
safe_param() {
local value
if [ $# -eq 1 ]; then
_param_value "$1"
# '+' is an encoded space: "a+b" -> "a b"
value="${REPLY//+/ }"
# delete the SAFE_STRIP characters: 'a$b"c' -> 'abc'
echo "${value//${SAFE_STRIP}/}"
else
param "$@"
fi
}

# cookie function. Same explanation as param
cookie() {
local name value
if [ $# -eq 1 ]; then
# "foo" and "COOKIE_foo" both give "COOKIE_foo"
name="COOKIE_${1#COOKIE_}"
case ${name} in
# empty name or not a valid variable name -> empty value
COOKIE_|COOKIE_*[!A-Za-z0-9_]*) value= ;;
# ${!name} is the value of the variable whose name is in $name
*) value="${!name}" ;;
esac
elif [ $# -gt 1 ]; then
name=$1
shift
eval "export 'COOKIE_${name}=$*'"
return
else
# ${!COOKIE_@} expands to every existing variable named COOKIE_*
for name in ${!COOKIE_@}; do
# "COOKIE_foo" -> "foo"
echo "${name#COOKIE_}"
done
return
fi
echo "${value}"
unset name
unset value
}

# keywords returns a list of keywords. This is only set when the script is
# called with an ISINDEX form (these are pretty rare nowadays).
keywords() {
echo "${KEYWORDS}"
}

set_cookie() {
local name=$1
shift
local value=$*
# "" -> "; a=1" -> "; a=1; b=2"
bashlib_cookies="${bashlib_cookies}; ${name}=${value}"

# drop the leading ';' only (the space after it stays): "; a=1" -> " a=1"
bashlib_cookies=${bashlib_cookies#;}

cookie "$name" "$value"
}

#
# send_redirect takes a URI and redirects the browser to that uri, exiting
# the script along the way.
#
send_redirect() {
local uri
if [ $# -eq 1 ]; then
uri=$1
else
uri="http://${SERVER_NAME}/${SCRIPT_NAME}"
fi
echo "Location: ${uri}"
echo ""
}
Loading