Commit 8a946ed80a0f6a58e23b9e5763e0d4d38a145320

Authored by Geoffrey Hunter
1 parent 01a18d8c

Added TravisCI config file and build script.

.travis.yml 0 → 100644
  1 +language: cpp
  2 +compiler: g++
  3 +
  4 +addons:
  5 + apt:
  6 + sources:
  7 + - llvm-toolchain-precise
  8 + - ubuntu-toolchain-r-test
  9 + packages:
  10 + - clang-3.7
  11 + - g++-5
  12 + - gcc-5
  13 +
  14 +install:
  15 + - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi
  16 + - if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.7" CC="clang-3.7"; fi
  17 +
  18 +script:
  19 + - ./tools/build.sh
0 20 \ No newline at end of file
... ...
CHANGELOG.md
... ... @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
9 9 ### Added
10 10 - Added CMake build support.
11 11 - Added basic, config and read/write unit tests using gtest.
  12 +- Improved read() performance due to removal of buffer creation on every call.
  13 +- TravisCI configuration file.
  14 +- Build script under `tools/`.
12 15  
13 16 ### Changed
14 17 - Updated serial port to use C++14.
... ...
include/CppLinuxSerial/SerialPort.hpp
... ... @@ -16,6 +16,7 @@
16 16 #include <fstream> // For file I/O (reading/writing to COM port)
17 17 #include <sstream>
18 18 #include <termios.h> // POSIX terminal control definitions (struct termios)
  19 +#include <vector>
19 20  
20 21 // User headers
21 22  
... ... @@ -67,10 +68,9 @@ namespace mn {
67 68 /// \param value Pass in true to enable echo, false to disable echo.
68 69 void SetEcho(bool value);
69 70  
70   - //! @brief Opens the COM port for use.
71   - //! @throws {std::runtime_error} if filename has not been set.
72   - //! {std::system_error} if system open() operation fails.
73   - //! @note Must call this before you can configure the COM port.
  71 + /// \brief Opens the COM port for use.
  72 + /// \throws CppLinuxSerial::Exception if device cannot be opened.
  73 + /// \note Must call this before you can configure the COM port.
74 74 void Open();
75 75  
76 76 /// \brief Closes the COM port.
... ... @@ -90,6 +90,9 @@ namespace mn {
90 90  
91 91 private:
92 92  
  93 + /// \brief Returns a populated termios structure for the passed in file descriptor.
  94 + termios GetTermios();
  95 +
93 96 /// \brief Configures the tty device as a serial port.
94 97 /// \warning Device must be open (valid file descriptor) when this is called.
95 98 void ConfigureTermios();
... ... @@ -112,11 +115,12 @@ namespace mn {
112 115  
113 116 int32_t timeout_ms_;
114 117  
115   - /// \brief Returns a populated termios structure for the passed in file descriptor.
116   - termios GetTermios();
  118 + std::vector<char> readBuffer_;
  119 + unsigned char readBufferSize_B_;
117 120  
118 121 static constexpr BaudRate defaultBaudRate_ = BaudRate::B_57600;
119 122 static constexpr int32_t defaultTimeout_ms_ = -1;
  123 + static constexpr unsigned char defaultReadBufferSize_B_ = 255;
120 124  
121 125  
122 126 };
... ...
src/SerialPort.cpp
... ... @@ -29,6 +29,8 @@ namespace CppLinuxSerial {
29 29 echo_ = false;
30 30 timeout_ms_ = defaultTimeout_ms_;
31 31 baudRate_ = defaultBaudRate_;
  32 + readBufferSize_B_ = defaultReadBufferSize_B_;
  33 + readBuffer_.reserve(readBufferSize_B_);
32 34 }
33 35  
34 36 SerialPort::SerialPort(const std::string& device, BaudRate baudRate) :
... ... @@ -65,10 +67,7 @@ namespace CppLinuxSerial {
65 67  
66 68 std::cout << "Attempting to open COM port \"" << device_ << "\"." << std::endl;
67 69  
68   - if(device_.size() == 0) {
69   - //this->sp->PrintError(SmartPrint::Ss() << "Attempted to open file when file path has not been assigned to.");
70   - //return false;
71   -
  70 + if(device_.empty()) {
72 71 THROW_EXCEPT("Attempted to open file when file path has not been assigned to.");
73 72 }
74 73  
... ... @@ -81,10 +80,6 @@ namespace CppLinuxSerial {
81 80  
82 81 // Check status
83 82 if(fileDesc_ == -1) {
84   - // Could not open COM port
85   - //this->sp->PrintError(SmartPrint::Ss() << "Unable to open " << this->filePath << " - " << strerror(errno));
86   - //return false;
87   -
88 83 THROW_EXCEPT("Could not open device " + device_ + ". Is the device name correct and do you have read/write permission?");
89 84 }
90 85  
... ... @@ -230,6 +225,8 @@ namespace CppLinuxSerial {
230 225  
231 226 void SerialPort::Read(std::string& data)
232 227 {
  228 + data.clear();
  229 +
233 230 if(fileDesc_ == 0) {
234 231 //this->sp->PrintError(SmartPrint::Ss() << "Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened.");
235 232 //return false;
... ... @@ -237,11 +234,14 @@ namespace CppLinuxSerial {
237 234 }
238 235  
239 236 // Allocate memory for read buffer
240   - char buf [256];
241   - memset (&buf, '\0', sizeof buf);
  237 +// char buf [256];
  238 +// memset (&buf, '\0', sizeof buf);
242 239  
243 240 // Read from file
244   - ssize_t n = read(fileDesc_, &buf, sizeof(buf));
  241 + // We provide the underlying raw array from the readBuffer_ vector to this C api.
  242 + // This will work because we do not delete/resize the vector while this method
  243 + // is called
  244 + ssize_t n = read(fileDesc_, &readBuffer_[0], readBufferSize_B_);
245 245  
246 246 // Error Handling
247 247 if(n < 0) {
... ... @@ -250,11 +250,11 @@ namespace CppLinuxSerial {
250 250 }
251 251  
252 252 if(n > 0) {
253   - //this->sp->PrintDebug(SmartPrint::Ss() << "\"" << n << "\" characters have been read from \"" << this->filePath << "\"");
254   - // Characters have been read
255   - buf[n] = '\0';
  253 +
  254 +// buf[n] = '\0';
256 255 //printf("%s\r\n", buf);
257   - data.append(buf);
  256 +// data.append(buf);
  257 + data = std::string(&readBuffer_[0], n);
258 258 //std::cout << *str << " and size of string =" << str->size() << "\r\n";
259 259 }
260 260  
... ...
tools/build.sh 0 → 100755
  1 +#!/usr/bin/env bash
  2 +
  3 +#
  4 +# \file build.sh
  5 +# \author Geoffrey Hunter (www.mbedded.ninja) <gbmhunter@gmail.com>
  6 +# \edited n/a
  7 +# \created 2017-09-27
  8 +# \last-modified 2017-11-27
  9 +# \brief Bash script for building/installing the source code.
  10 +# \details
  11 +# See README.md in root dir for more info.
  12 +
  13 +# Get script path
  14 +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  15 +
  16 +# 3rd party imports
  17 +. ${script_dir}/lib/shflags
  18 +
  19 +# User imports
  20 +. ${script_dir}/lib/utilities.sh
  21 +
  22 +printInfo "=========================================================================================="
  23 +printInfo "================================= CppLinuxSerial build.sh ================================"
  24 +printInfo "=========================================================================================="
  25 +
  26 +set +e
  27 +
  28 +# Define the command-line arguments
  29 +DEFINE_boolean 'install' 'false' 'Do you want to [i]nstall the CppLinuxSerial header files onto your local system after build?' 'i'
  30 +
  31 +# parse the command-line
  32 +FLAGS "$@" || exit 1
  33 +eval set -- "${FLAGS_ARGV}"
  34 +
  35 +# Any subsequent commands which fail will cause the shell script to exit immediately
  36 +# WARNING: Make sure to only activate this AFTER shflags has parsed command-line arguments
  37 +set -e
  38 +
  39 +printInfo "install = ${FLAGS_install}"
  40 +
  41 +BUILD_DIRECTORY_NAME="build"
  42 +
  43 +# This will only make the build directory if it doesn't already
  44 +# exist. If it does exist, there is likely to be build artifacts
  45 +# in there already.
  46 +printInfo "Making and/or changing into build directory (${script_dir}/../${BUILD_DIRECTORY_NAME}/)..."
  47 +mkdir -p ${script_dir}/../${BUILD_DIRECTORY_NAME}/
  48 +cd ${script_dir}/../${BUILD_DIRECTORY_NAME}/
  49 +
  50 +printInfo 'Invoking cmake...'
  51 +cmake ..
  52 +
  53 +printInfo 'Invoking make...'
  54 +make -j8
  55 +
  56 +printInfo 'Running unit tests...'
  57 +make -j8 run_unit_tests
  58 +
  59 +if [[ "$FLAGS_install" == $FLAGS_TRUE ]]; then
  60 + printInfo "Installing CppLinuxSerial headers onto local system..."
  61 + sudo make install
  62 +fi
... ...
tools/lib/shflags 0 → 100755
  1 +# vim:et:ft=sh:sts=2:sw=2
  2 +#
  3 +# Copyright 2008-2016 Kate Ward. All Rights Reserved.
  4 +# Released under the Apache License 2.0.
  5 +#
  6 +# shFlags -- Advanced command-line flag library for Unix shell scripts.
  7 +# http://code.google.com/p/shflags/
  8 +#
  9 +# Author: kate.ward@forestent.com (Kate Ward)
  10 +#
  11 +# This module implements something like the google-gflags library available
  12 +# from http://code.google.com/p/google-gflags/.
  13 +#
  14 +# FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take
  15 +# a name, default value, help-string, and optional 'short' name (one-letter
  16 +# name). Some flags have other arguments, which are described with the flag.
  17 +#
  18 +# DEFINE_string: takes any input, and intreprets it as a string.
  19 +#
  20 +# DEFINE_boolean: does not take any arguments. Say --myflag to set
  21 +# FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false. For short
  22 +# flags, passing the flag on the command-line negates the default value, i.e.
  23 +# if the default is true, passing the flag sets the value to false.
  24 +#
  25 +# DEFINE_float: takes an input and intreprets it as a floating point number. As
  26 +# shell does not support floats per-se, the input is merely validated as
  27 +# being a valid floating point value.
  28 +#
  29 +# DEFINE_integer: takes an input and intreprets it as an integer.
  30 +#
  31 +# SPECIAL FLAGS: There are a few flags that have special meaning:
  32 +# --help (or -?) prints a list of all the flags in a human-readable fashion
  33 +# --flagfile=foo read flags from foo. (not implemented yet)
  34 +# -- as in getopt(), terminates flag-processing
  35 +#
  36 +# EXAMPLE USAGE:
  37 +#
  38 +# -- begin hello.sh --
  39 +# #! /bin/sh
  40 +# . ./shflags
  41 +# DEFINE_string name 'world' "somebody's name" n
  42 +# FLAGS "$@" || exit $?
  43 +# eval set -- "${FLAGS_ARGV}"
  44 +# echo "Hello, ${FLAGS_name}."
  45 +# -- end hello.sh --
  46 +#
  47 +# $ ./hello.sh -n Kate
  48 +# Hello, Kate.
  49 +#
  50 +# CUSTOMIZABLE BEHAVIOR:
  51 +#
  52 +# A script can override the default 'getopt' command by providing the path to
  53 +# an alternate implementation by defining the FLAGS_GETOPT_CMD variable.
  54 +#
  55 +# NOTES:
  56 +#
  57 +# * Not all systems include a getopt version that supports long flags. On these
  58 +# systems, only short flags are recognized.
  59 +
  60 +#==============================================================================
  61 +# shFlags
  62 +#
  63 +# Shared attributes:
  64 +# flags_error: last error message
  65 +# flags_output: last function output (rarely valid)
  66 +# flags_return: last return value
  67 +#
  68 +# __flags_longNames: list of long names for all flags
  69 +# __flags_shortNames: list of short names for all flags
  70 +# __flags_boolNames: list of boolean flag names
  71 +#
  72 +# __flags_opts: options parsed by getopt
  73 +#
  74 +# Per-flag attributes:
  75 +# FLAGS_<flag_name>: contains value of flag named 'flag_name'
  76 +# __flags_<flag_name>_default: the default flag value
  77 +# __flags_<flag_name>_help: the flag help string
  78 +# __flags_<flag_name>_short: the flag short name
  79 +# __flags_<flag_name>_type: the flag type
  80 +#
  81 +# Notes:
  82 +# - lists of strings are space separated, and a null value is the '~' char.
  83 +
  84 +# return if FLAGS already loaded
  85 +[ -n "${FLAGS_VERSION:-}" ] && return 0
  86 +FLAGS_VERSION='1.2.0'
  87 +
  88 +# return values that scripts can use
  89 +FLAGS_TRUE=0
  90 +FLAGS_FALSE=1
  91 +FLAGS_ERROR=2
  92 +
  93 +# determine some reasonable command defaults
  94 +__FLAGS_UNAME_S=`uname -s`
  95 +case "${__FLAGS_UNAME_S}" in
  96 + BSD) __FLAGS_EXPR_CMD='gexpr' ;;
  97 + *) __FLAGS_EXPR_CMD='expr' ;;
  98 +esac
  99 +
  100 +# commands a user can override if needed
  101 +FLAGS_EXPR_CMD=${FLAGS_EXPR_CMD:-${__FLAGS_EXPR_CMD}}
  102 +FLAGS_GETOPT_CMD=${FLAGS_GETOPT_CMD:-getopt}
  103 +
  104 +# specific shell checks
  105 +if [ -n "${ZSH_VERSION:-}" ]; then
  106 + setopt |grep "^shwordsplit$" >/dev/null
  107 + if [ $? -ne ${FLAGS_TRUE} ]; then
  108 + _flags_fatal 'zsh shwordsplit option is required for proper zsh operation'
  109 + fi
  110 + if [ -z "${FLAGS_PARENT:-}" ]; then
  111 + _flags_fatal "zsh does not pass \$0 through properly. please declare' \
  112 +\"FLAGS_PARENT=\$0\" before calling shFlags"
  113 + fi
  114 +fi
  115 +
  116 +# can we use built-ins?
  117 +( echo "${FLAGS_TRUE#0}"; ) >/dev/null 2>&1
  118 +if [ $? -eq ${FLAGS_TRUE} ]; then
  119 + __FLAGS_USE_BUILTIN=${FLAGS_TRUE}
  120 +else
  121 + __FLAGS_USE_BUILTIN=${FLAGS_FALSE}
  122 +fi
  123 +
  124 +#
  125 +# constants
  126 +#
  127 +
  128 +# reserved flag names
  129 +__FLAGS_RESERVED_LIST=' ARGC ARGV ERROR FALSE GETOPT_CMD HELP PARENT TRUE '
  130 +__FLAGS_RESERVED_LIST="${__FLAGS_RESERVED_LIST} VERSION "
  131 +
  132 +# getopt version
  133 +__FLAGS_GETOPT_VERS_STD=0
  134 +__FLAGS_GETOPT_VERS_ENH=1
  135 +__FLAGS_GETOPT_VERS_BSD=2
  136 +
  137 +${FLAGS_GETOPT_CMD} >/dev/null 2>&1
  138 +case $? in
  139 + 0) __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} ;; # bsd getopt
  140 + 2)
  141 + # TODO(kward): look into '-T' option to test the internal getopt() version
  142 + if [ "`${FLAGS_GETOPT_CMD} --version`" = '-- ' ]; then
  143 + __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD}
  144 + else
  145 + __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_ENH}
  146 + fi
  147 + ;;
  148 + *) _flags_fatal 'unable to determine getopt version' ;;
  149 +esac
  150 +
  151 +# getopt optstring lengths
  152 +__FLAGS_OPTSTR_SHORT=0
  153 +__FLAGS_OPTSTR_LONG=1
  154 +
  155 +__FLAGS_NULL='~'
  156 +
  157 +# flag info strings
  158 +__FLAGS_INFO_DEFAULT='default'
  159 +__FLAGS_INFO_HELP='help'
  160 +__FLAGS_INFO_SHORT='short'
  161 +__FLAGS_INFO_TYPE='type'
  162 +
  163 +# flag lengths
  164 +__FLAGS_LEN_SHORT=0
  165 +__FLAGS_LEN_LONG=1
  166 +
  167 +# flag types
  168 +__FLAGS_TYPE_NONE=0
  169 +__FLAGS_TYPE_BOOLEAN=1
  170 +__FLAGS_TYPE_FLOAT=2
  171 +__FLAGS_TYPE_INTEGER=3
  172 +__FLAGS_TYPE_STRING=4
  173 +
  174 +# set the constants readonly
  175 +__flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'`
  176 +for __flags_const in ${__flags_constants}; do
  177 + # skip certain flags
  178 + case ${__flags_const} in
  179 + FLAGS_HELP) continue ;;
  180 + FLAGS_PARENT) continue ;;
  181 + esac
  182 + # set flag readonly
  183 + if [ -z "${ZSH_VERSION:-}" ]; then
  184 + readonly ${__flags_const}
  185 + else # handle zsh
  186 + case ${ZSH_VERSION} in
  187 + [123].*) readonly ${__flags_const} ;;
  188 + *) readonly -g ${__flags_const} ;; # declare readonly constants globally
  189 + esac
  190 + fi
  191 +done
  192 +unset __flags_const __flags_constants
  193 +
  194 +#
  195 +# internal variables
  196 +#
  197 +
  198 +# space separated lists
  199 +__flags_boolNames=' ' # boolean flag names
  200 +__flags_longNames=' ' # long flag names
  201 +__flags_shortNames=' ' # short flag names
  202 +__flags_definedNames=' ' # defined flag names (used for validation)
  203 +
  204 +__flags_columns='' # screen width in columns
  205 +__flags_opts='' # temporary storage for parsed getopt flags
  206 +
  207 +#------------------------------------------------------------------------------
  208 +# private functions
  209 +#
  210 +
  211 +# logging functions
  212 +_flags_debug() { echo "flags:DEBUG $@" >&2; }
  213 +_flags_warn() { echo "flags:WARN $@" >&2; }
  214 +_flags_error() { echo "flags:ERROR $@" >&2; }
  215 +_flags_fatal() { echo "flags:FATAL $@" >&2; exit ${FLAGS_ERROR}; }
  216 +
  217 +# Define a flag.
  218 +#
  219 +# Calling this function will define the following info variables for the
  220 +# specified flag:
  221 +# FLAGS_flagname - the name for this flag (based upon the long flag name)
  222 +# __flags_<flag_name>_default - the default value
  223 +# __flags_flagname_help - the help string
  224 +# __flags_flagname_short - the single letter alias
  225 +# __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*)
  226 +#
  227 +# Args:
  228 +# _flags__type: integer: internal type of flag (__FLAGS_TYPE_*)
  229 +# _flags__name: string: long flag name
  230 +# _flags__default: default flag value
  231 +# _flags__help: string: help string
  232 +# _flags__short: string: (optional) short flag name
  233 +# Returns:
  234 +# integer: success of operation, or error
  235 +_flags_define()
  236 +{
  237 + if [ $# -lt 4 ]; then
  238 + flags_error='DEFINE error: too few arguments'
  239 + flags_return=${FLAGS_ERROR}
  240 + _flags_error "${flags_error}"
  241 + return ${flags_return}
  242 + fi
  243 +
  244 + _flags_type_=$1
  245 + _flags_name_=$2
  246 + _flags_default_=$3
  247 + _flags_help_=$4
  248 + _flags_short_=${5:-${__FLAGS_NULL}}
  249 +
  250 + _flags_return_=${FLAGS_TRUE}
  251 + _flags_usName_=`_flags_underscoreName ${_flags_name_}`
  252 +
  253 + # check whether the flag name is reserved
  254 + _flags_itemInList ${_flags_usName_} "${__FLAGS_RESERVED_LIST}"
  255 + if [ $? -eq ${FLAGS_TRUE} ]; then
  256 + flags_error="flag name (${_flags_name_}) is reserved"
  257 + _flags_return_=${FLAGS_ERROR}
  258 + fi
  259 +
  260 + # require short option for getopt that don't support long options
  261 + if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
  262 + -a ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} \
  263 + -a "${_flags_short_}" = "${__FLAGS_NULL}" ]
  264 + then
  265 + flags_error="short flag required for (${_flags_name_}) on this platform"
  266 + _flags_return_=${FLAGS_ERROR}
  267 + fi
  268 +
  269 + # check for existing long name definition
  270 + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
  271 + if _flags_itemInList ${_flags_usName_} ${__flags_definedNames}; then
  272 + flags_error="definition for ([no]${_flags_name_}) already exists"
  273 + _flags_warn "${flags_error}"
  274 + _flags_return_=${FLAGS_FALSE}
  275 + fi
  276 + fi
  277 +
  278 + # check for existing short name definition
  279 + if [ ${_flags_return_} -eq ${FLAGS_TRUE} \
  280 + -a "${_flags_short_}" != "${__FLAGS_NULL}" ]
  281 + then
  282 + if _flags_itemInList "${_flags_short_}" ${__flags_shortNames}; then
  283 + flags_error="flag short name (${_flags_short_}) already defined"
  284 + _flags_warn "${flags_error}"
  285 + _flags_return_=${FLAGS_FALSE}
  286 + fi
  287 + fi
  288 +
  289 + # handle default value. note, on several occasions the 'if' portion of an
  290 + # if/then/else contains just a ':' which does nothing. a binary reversal via
  291 + # '!' is not done because it does not work on all shells.
  292 + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
  293 + case ${_flags_type_} in
  294 + ${__FLAGS_TYPE_BOOLEAN})
  295 + if _flags_validBool "${_flags_default_}"; then
  296 + case ${_flags_default_} in
  297 + true|t|0) _flags_default_=${FLAGS_TRUE} ;;
  298 + false|f|1) _flags_default_=${FLAGS_FALSE} ;;
  299 + esac
  300 + else
  301 + flags_error="invalid default flag value '${_flags_default_}'"
  302 + _flags_return_=${FLAGS_ERROR}
  303 + fi
  304 + ;;
  305 +
  306 + ${__FLAGS_TYPE_FLOAT})
  307 + if _flags_validFloat "${_flags_default_}"; then
  308 + :
  309 + else
  310 + flags_error="invalid default flag value '${_flags_default_}'"
  311 + _flags_return_=${FLAGS_ERROR}
  312 + fi
  313 + ;;
  314 +
  315 + ${__FLAGS_TYPE_INTEGER})
  316 + if _flags_validInt "${_flags_default_}"; then
  317 + :
  318 + else
  319 + flags_error="invalid default flag value '${_flags_default_}'"
  320 + _flags_return_=${FLAGS_ERROR}
  321 + fi
  322 + ;;
  323 +
  324 + ${__FLAGS_TYPE_STRING}) ;; # everything in shell is a valid string
  325 +
  326 + *)
  327 + flags_error="unrecognized flag type '${_flags_type_}'"
  328 + _flags_return_=${FLAGS_ERROR}
  329 + ;;
  330 + esac
  331 + fi
  332 +
  333 + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then
  334 + # store flag information
  335 + eval "FLAGS_${_flags_usName_}='${_flags_default_}'"
  336 + eval "__flags_${_flags_usName_}_${__FLAGS_INFO_TYPE}=${_flags_type_}"
  337 + eval "__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}=\
  338 +\"${_flags_default_}\""
  339 + eval "__flags_${_flags_usName_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\""
  340 + eval "__flags_${_flags_usName_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'"
  341 +
  342 + # append flag names to name lists
  343 + __flags_shortNames="${__flags_shortNames}${_flags_short_} "
  344 + __flags_longNames="${__flags_longNames}${_flags_name_} "
  345 + [ ${_flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] && \
  346 + __flags_boolNames="${__flags_boolNames}no${_flags_name_} "
  347 +
  348 + # append flag names to defined names for later validation checks
  349 + __flags_definedNames="${__flags_definedNames}${_flags_usName_} "
  350 + [ ${_flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] && \
  351 + __flags_definedNames="${__flags_definedNames}no${_flags_usName_} "
  352 + fi
  353 +
  354 + flags_return=${_flags_return_}
  355 + unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ \
  356 + _flags_short_ _flags_type_ _flags_usName_
  357 + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
  358 + return ${flags_return}
  359 +}
  360 +
  361 +# Underscore a flag name by replacing dashes with underscores.
  362 +#
  363 +# Args:
  364 +# unnamed: string: log flag name
  365 +# Output:
  366 +# string: underscored name
  367 +_flags_underscoreName()
  368 +{
  369 + echo $1 |tr '-' '_'
  370 +}
  371 +
  372 +# Return valid getopt options using currently defined list of long options.
  373 +#
  374 +# This function builds a proper getopt option string for short (and long)
  375 +# options, using the current list of long options for reference.
  376 +#
  377 +# Args:
  378 +# _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*)
  379 +# Output:
  380 +# string: generated option string for getopt
  381 +# Returns:
  382 +# boolean: success of operation (always returns True)
  383 +_flags_genOptStr()
  384 +{
  385 + _flags_optStrType_=$1
  386 +
  387 + _flags_opts_=''
  388 +
  389 + for _flags_name_ in ${__flags_longNames}; do
  390 + _flags_usName_=`_flags_underscoreName ${_flags_name_}`
  391 + _flags_type_=`_flags_getFlagInfo ${_flags_usName_} ${__FLAGS_INFO_TYPE}`
  392 + [ $? -eq ${FLAGS_TRUE} ] || _flags_fatal 'call to _flags_type_ failed'
  393 + case ${_flags_optStrType_} in
  394 + ${__FLAGS_OPTSTR_SHORT})
  395 + _flags_shortName_=`_flags_getFlagInfo \
  396 + ${_flags_usName_} ${__FLAGS_INFO_SHORT}`
  397 + if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then
  398 + _flags_opts_="${_flags_opts_}${_flags_shortName_}"
  399 + # getopt needs a trailing ':' to indicate a required argument
  400 + [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
  401 + _flags_opts_="${_flags_opts_}:"
  402 + fi
  403 + ;;
  404 +
  405 + ${__FLAGS_OPTSTR_LONG})
  406 + _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_name_}"
  407 + # getopt needs a trailing ':' to indicate a required argument
  408 + [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \
  409 + _flags_opts_="${_flags_opts_}:"
  410 + ;;
  411 + esac
  412 + done
  413 +
  414 + echo "${_flags_opts_}"
  415 + unset _flags_name_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \
  416 + _flags_type_ _flags_usName_
  417 + return ${FLAGS_TRUE}
  418 +}
  419 +
  420 +# Returns flag details based on a flag name and flag info.
  421 +#
  422 +# Args:
  423 +# string: underscored flag name
  424 +# string: flag info (see the _flags_define function for valid info types)
  425 +# Output:
  426 +# string: value of dereferenced flag variable
  427 +# Returns:
  428 +# integer: one of FLAGS_{TRUE|FALSE|ERROR}
  429 +_flags_getFlagInfo()
  430 +{
  431 + # note: adding gFI to variable names to prevent naming conflicts with calling
  432 + # functions
  433 + _flags_gFI_usName_=$1
  434 + _flags_gFI_info_=$2
  435 +
  436 + _flags_infoVar_="__flags_${_flags_gFI_usName_}_${_flags_gFI_info_}"
  437 + _flags_strToEval_="_flags_infoValue_=\"\${${_flags_infoVar_}:-}\""
  438 + eval "${_flags_strToEval_}"
  439 + if [ -n "${_flags_infoValue_}" ]; then
  440 + flags_return=${FLAGS_TRUE}
  441 + else
  442 + # see if the _flags_gFI_usName_ variable is a string as strings can be
  443 + # empty...
  444 + # note: the DRY principle would say to have this function call itself for
  445 + # the next three lines, but doing so results in an infinite loop as an
  446 + # invalid _flags_name_ will also not have the associated _type variable.
  447 + # Because it doesn't (it will evaluate to an empty string) the logic will
  448 + # try to find the _type variable of the _type variable, and so on. Not so
  449 + # good ;-)
  450 + _flags_typeVar_="__flags_${_flags_gFI_usName_}_${__FLAGS_INFO_TYPE}"
  451 + _flags_strToEval_="_flags_typeValue_=\"\${${_flags_typeVar_}:-}\""
  452 + eval "${_flags_strToEval_}"
  453 + if [ "${_flags_typeValue_}" = "${__FLAGS_TYPE_STRING}" ]; then
  454 + flags_return=${FLAGS_TRUE}
  455 + else
  456 + flags_return=${FLAGS_ERROR}
  457 + flags_error="missing flag info variable (${_flags_infoVar_})"
  458 + fi
  459 + fi
  460 +
  461 + echo "${_flags_infoValue_}"
  462 + unset _flags_gFI_usName_ _flags_gfI_info_ _flags_infoValue_ _flags_infoVar_ \
  463 + _flags_strToEval_ _flags_typeValue_ _flags_typeVar_
  464 + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}"
  465 + return ${flags_return}
  466 +}
  467 +
  468 +# Check for presense of item in a list.
  469 +#
  470 +# Passed a string (e.g. 'abc'), this function will determine if the string is
  471 +# present in the list of strings (e.g. ' foo bar abc ').
  472 +#
  473 +# Args:
  474 +# _flags_str_: string: string to search for in a list of strings
  475 +# unnamed: list: list of strings
  476 +# Returns:
  477 +# boolean: true if item is in the list
  478 +_flags_itemInList() {
  479 + _flags_str_=$1
  480 + shift
  481 +
  482 + echo " ${*:-} " |grep " ${_flags_str_} " >/dev/null
  483 + if [ $? -eq 0 ]; then
  484 + flags_return=${FLAGS_TRUE}
  485 + else
  486 + flags_return=${FLAGS_FALSE}
  487 + fi
  488 +
  489 + unset _flags_str_
  490 + return ${flags_return}
  491 +}
  492 +
  493 +# Returns the width of the current screen.
  494 +#
  495 +# Output:
  496 +# integer: width in columns of the current screen.
  497 +_flags_columns()
  498 +{
  499 + if [ -z "${__flags_columns}" ]; then
  500 + # determine the value and store it
  501 + if eval stty size >/dev/null 2>&1; then
  502 + # stty size worked :-)
  503 + set -- `stty size`
  504 + __flags_columns=$2
  505 + elif eval tput cols >/dev/null 2>&1; then
  506 + set -- `tput cols`
  507 + __flags_columns=$1
  508 + else
  509 + __flags_columns=80 # default terminal width
  510 + fi
  511 + fi
  512 + echo ${__flags_columns}
  513 +}
  514 +
  515 +# Validate a boolean.
  516 +#
  517 +# Args:
  518 +# _flags__bool: boolean: value to validate
  519 +# Returns:
  520 +# bool: true if the value is a valid boolean
  521 +_flags_validBool()
  522 +{
  523 + _flags_bool_=$1
  524 +
  525 + flags_return=${FLAGS_TRUE}
  526 + case "${_flags_bool_}" in
  527 + true|t|0) ;;
  528 + false|f|1) ;;
  529 + *) flags_return=${FLAGS_FALSE} ;;
  530 + esac
  531 +
  532 + unset _flags_bool_
  533 + return ${flags_return}
  534 +}
  535 +
  536 +# Validate a float.
  537 +#
  538 +# Args:
  539 +# _flags_float_: float: value to validate
  540 +# Returns:
  541 +# bool: true if the value is a valid integer
  542 +_flags_validFloat()
  543 +{
  544 + flags_return=${FLAGS_FALSE}
  545 + [ -n "$1" ] || return ${flags_return}
  546 + _flags_float_=$1
  547 +
  548 + if _flags_validInt ${_flags_float_}; then
  549 + flags_return=${FLAGS_TRUE}
  550 + elif _flags_useBuiltin; then
  551 + _flags_float_whole_=${_flags_float_%.*}
  552 + _flags_float_fraction_=${_flags_float_#*.}
  553 + if _flags_validInt ${_flags_float_whole_:-0} -a \
  554 + _flags_validInt ${_flags_float_fraction_}; then
  555 + flags_return=${FLAGS_TRUE}
  556 + fi
  557 + unset _flags_float_whole_ _flags_float_fraction_
  558 + else
  559 + flags_return=${FLAGS_TRUE}
  560 + case ${_flags_float_} in
  561 + -*) # negative floats
  562 + _flags_test_=`${FLAGS_EXPR_CMD} -- "${_flags_float_}" :\
  563 + '\(-[0-9]*\.[0-9]*\)'`
  564 + ;;
  565 + *) # positive floats
  566 + _flags_test_=`${FLAGS_EXPR_CMD} -- "${_flags_float_}" :\
  567 + '\([0-9]*\.[0-9]*\)'`
  568 + ;;
  569 + esac
  570 + [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE}
  571 + unset _flags_test_
  572 + fi
  573 +
  574 + unset _flags_float_ _flags_float_whole_ _flags_float_fraction_
  575 + return ${flags_return}
  576 +}
  577 +
  578 +# Validate an integer.
  579 +#
  580 +# Args:
  581 +# _flags_int_: integer: value to validate
  582 +# Returns:
  583 +# bool: true if the value is a valid integer
  584 +_flags_validInt()
  585 +{
  586 + flags_return=${FLAGS_FALSE}
  587 + [ -n "$1" ] || return ${flags_return}
  588 + _flags_int_=$1
  589 +
  590 + case ${_flags_int_} in
  591 + -*.*) ;; # ignore negative floats (we'll invalidate them later)
  592 + -*) # strip possible leading negative sign
  593 + if _flags_useBuiltin; then
  594 + _flags_int_=${_flags_int_#-}
  595 + else
  596 + _flags_int_=`${FLAGS_EXPR_CMD} -- "${_flags_int_}" : '-\([0-9][0-9]*\)'`
  597 + fi
  598 + ;;
  599 + esac
  600 +
  601 + case ${_flags_int_} in
  602 + *[!0-9]*) flags_return=${FLAGS_FALSE} ;;
  603 + *) flags_return=${FLAGS_TRUE} ;;
  604 + esac
  605 +
  606 + unset _flags_int_
  607 + return ${flags_return}
  608 +}
  609 +
  610 +# Parse command-line options using the standard getopt.
  611 +#
  612 +# Note: the flag options are passed around in the global __flags_opts so that
  613 +# the formatting is not lost due to shell parsing and such.
  614 +#
  615 +# Args:
  616 +# @: varies: command-line options to parse
  617 +# Returns:
  618 +# integer: a FLAGS success condition
  619 +_flags_getoptStandard()
  620 +{
  621 + flags_return=${FLAGS_TRUE}
  622 + _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
  623 +
  624 + # check for spaces in passed options
  625 + for _flags_opt_ in "$@"; do
  626 + # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
  627 + _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'`
  628 + if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then
  629 + flags_error='the available getopt does not support spaces in options'
  630 + flags_return=${FLAGS_ERROR}
  631 + break
  632 + fi
  633 + done
  634 +
  635 + if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
  636 + __flags_opts=`getopt ${_flags_shortOpts_} $@ 2>&1`
  637 + _flags_rtrn_=$?
  638 + if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
  639 + _flags_warn "${__flags_opts}"
  640 + flags_error='unable to parse provided options with getopt.'
  641 + flags_return=${FLAGS_ERROR}
  642 + fi
  643 + fi
  644 +
  645 + unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_
  646 + return ${flags_return}
  647 +}
  648 +
  649 +# Parse command-line options using the enhanced getopt.
  650 +#
  651 +# Note: the flag options are passed around in the global __flags_opts so that
  652 +# the formatting is not lost due to shell parsing and such.
  653 +#
  654 +# Args:
  655 +# @: varies: command-line options to parse
  656 +# Returns:
  657 +# integer: a FLAGS success condition
  658 +_flags_getoptEnhanced()
  659 +{
  660 + flags_return=${FLAGS_TRUE}
  661 + _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}`
  662 + _flags_boolOpts_=`echo "${__flags_boolNames}" \
  663 + |sed 's/^ *//;s/ *$//;s/ /,/g'`
  664 + _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}`
  665 +
  666 + __flags_opts=`${FLAGS_GETOPT_CMD} \
  667 + -o ${_flags_shortOpts_} \
  668 + -l "${_flags_longOpts_},${_flags_boolOpts_}" \
  669 + -- "$@" 2>&1`
  670 + _flags_rtrn_=$?
  671 + if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then
  672 + _flags_warn "${__flags_opts}"
  673 + flags_error='unable to parse provided options with getopt.'
  674 + flags_return=${FLAGS_ERROR}
  675 + fi
  676 +
  677 + unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_
  678 + return ${flags_return}
  679 +}
  680 +
  681 +# Dynamically parse a getopt result and set appropriate variables.
  682 +#
  683 +# This function does the actual conversion of getopt output and runs it through
  684 +# the standard case structure for parsing. The case structure is actually quite
  685 +# dynamic to support any number of flags.
  686 +#
  687 +# Args:
  688 +# argc: int: original command-line argument count
  689 +# @: varies: output from getopt parsing
  690 +# Returns:
  691 +# integer: a FLAGS success condition
  692 +_flags_parseGetopt()
  693 +{
  694 + _flags_argc_=$1
  695 + shift
  696 +
  697 + flags_return=${FLAGS_TRUE}
  698 +
  699 + if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
  700 + set -- $@
  701 + else
  702 + # note the quotes around the `$@' -- they are essential!
  703 + eval set -- "$@"
  704 + fi
  705 +
  706 + # Provide user with the number of arguments to shift by later.
  707 + # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not
  708 + # properly give user access to non-flag arguments mixed in between flag
  709 + # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only
  710 + # for backwards compatibility reasons.
  711 + FLAGS_ARGC=`_flags_math "$# - 1 - ${_flags_argc_}"`
  712 +
  713 + # handle options. note options with values must do an additional shift
  714 + while true; do
  715 + _flags_opt_=$1
  716 + _flags_arg_=${2:-}
  717 + _flags_type_=${__FLAGS_TYPE_NONE}
  718 + _flags_name_=''
  719 +
  720 + # determine long flag name
  721 + case "${_flags_opt_}" in
  722 + --) shift; break ;; # discontinue option parsing
  723 +
  724 + --*) # long option
  725 + if _flags_useBuiltin; then
  726 + _flags_opt_=${_flags_opt_#*--}
  727 + else
  728 + _flags_opt_=`${FLAGS_EXPR_CMD} -- "${_flags_opt_}" : '--\(.*\)'`
  729 + fi
  730 + _flags_len_=${__FLAGS_LEN_LONG}
  731 + if _flags_itemInList "${_flags_opt_}" ${__flags_longNames}; then
  732 + _flags_name_=${_flags_opt_}
  733 + else
  734 + # check for negated long boolean version
  735 + if _flags_itemInList "${_flags_opt_}" ${__flags_boolNames}; then
  736 + if _flags_useBuiltin; then
  737 + _flags_name_=${_flags_opt_#*no}
  738 + else
  739 + _flags_name_=`${FLAGS_EXPR_CMD} -- "${_flags_opt_}" : 'no\(.*\)'`
  740 + fi
  741 + _flags_type_=${__FLAGS_TYPE_BOOLEAN}
  742 + _flags_arg_=${__FLAGS_NULL}
  743 + fi
  744 + fi
  745 + ;;
  746 +
  747 + -*) # short option
  748 + if _flags_useBuiltin; then
  749 + _flags_opt_=${_flags_opt_#*-}
  750 + else
  751 + _flags_opt_=`${FLAGS_EXPR_CMD} -- "${_flags_opt_}" : '-\(.*\)'`
  752 + fi
  753 + _flags_len_=${__FLAGS_LEN_SHORT}
  754 + if _flags_itemInList "${_flags_opt_}" ${__flags_shortNames}; then
  755 + # yes. match short name to long name. note purposeful off-by-one
  756 + # (too high) with awk calculations.
  757 + _flags_pos_=`echo "${__flags_shortNames}" \
  758 + |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \
  759 + e=${_flags_opt_}`
  760 + _flags_name_=`echo "${__flags_longNames}" \
  761 + |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"`
  762 + fi
  763 + ;;
  764 + esac
  765 +
  766 + # die if the flag was unrecognized
  767 + if [ -z "${_flags_name_}" ]; then
  768 + flags_error="unrecognized option (${_flags_opt_})"
  769 + flags_return=${FLAGS_ERROR}
  770 + break
  771 + fi
  772 +
  773 + # set new flag value
  774 + _flags_usName_=`_flags_underscoreName ${_flags_name_}`
  775 + [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \
  776 + _flags_type_=`_flags_getFlagInfo \
  777 + "${_flags_usName_}" ${__FLAGS_INFO_TYPE}`
  778 + case ${_flags_type_} in
  779 + ${__FLAGS_TYPE_BOOLEAN})
  780 + if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then
  781 + if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then
  782 + eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}"
  783 + else
  784 + eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}"
  785 + fi
  786 + else
  787 + _flags_strToEval_="_flags_val_=\
  788 +\${__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}}"
  789 + eval "${_flags_strToEval_}"
  790 + if [ ${_flags_val_} -eq ${FLAGS_FALSE} ]; then
  791 + eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}"
  792 + else
  793 + eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}"
  794 + fi
  795 + fi
  796 + ;;
  797 +
  798 + ${__FLAGS_TYPE_FLOAT})
  799 + if _flags_validFloat "${_flags_arg_}"; then
  800 + eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
  801 + else
  802 + flags_error="invalid float value (${_flags_arg_})"
  803 + flags_return=${FLAGS_ERROR}
  804 + break
  805 + fi
  806 + ;;
  807 +
  808 + ${__FLAGS_TYPE_INTEGER})
  809 + if _flags_validInt "${_flags_arg_}"; then
  810 + eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
  811 + else
  812 + flags_error="invalid integer value (${_flags_arg_})"
  813 + flags_return=${FLAGS_ERROR}
  814 + break
  815 + fi
  816 + ;;
  817 +
  818 + ${__FLAGS_TYPE_STRING})
  819 + eval "FLAGS_${_flags_usName_}='${_flags_arg_}'"
  820 + ;;
  821 + esac
  822 +
  823 + # handle special case help flag
  824 + if [ "${_flags_usName_}" = 'help' ]; then
  825 + if [ ${FLAGS_help} -eq ${FLAGS_TRUE} ]; then
  826 + flags_help
  827 + flags_error='help requested'
  828 + flags_return=${FLAGS_FALSE}
  829 + break
  830 + fi
  831 + fi
  832 +
  833 + # shift the option and non-boolean arguements out.
  834 + shift
  835 + [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ] && shift
  836 + done
  837 +
  838 + # give user back non-flag arguments
  839 + FLAGS_ARGV=''
  840 + while [ $# -gt 0 ]; do
  841 + FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'"
  842 + shift
  843 + done
  844 +
  845 + unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \
  846 + _flags_strToEval_ _flags_type_ _flags_usName_ _flags_val_
  847 + return ${flags_return}
  848 +}
  849 +
  850 +# Perform some path using built-ins.
  851 +#
  852 +# Args:
  853 +# $@: string: math expression to evaluate
  854 +# Output:
  855 +# integer: the result
  856 +# Returns:
  857 +# bool: success of math evaluation
  858 +_flags_math()
  859 +{
  860 + if [ $# -eq 0 ]; then
  861 + flags_return=${FLAGS_FALSE}
  862 + elif _flags_useBuiltin; then
  863 + # Variable assignment is needed as workaround for Solaris Bourne shell,
  864 + # which cannot parse a bare $((expression)).
  865 + _flags_expr_='$(($@))'
  866 + eval echo ${_flags_expr_}
  867 + flags_return=$?
  868 + unset _flags_expr_
  869 + else
  870 + eval expr $@
  871 + flags_return=$?
  872 + fi
  873 +
  874 + return ${flags_return}
  875 +}
  876 +
  877 +# Cross-platform strlen() implementation.
  878 +#
  879 +# Args:
  880 +# _flags_str: string: to determine length of
  881 +# Output:
  882 +# integer: length of string
  883 +# Returns:
  884 +# bool: success of strlen evaluation
  885 +_flags_strlen()
  886 +{
  887 + _flags_str_=${1:-}
  888 +
  889 + if [ -z "${_flags_str_}" ]; then
  890 + flags_output=0
  891 + elif _flags_useBuiltin; then
  892 + flags_output=${#_flags_str_}
  893 + else
  894 + flags_output=`${FLAGS_EXPR_CMD} -- "${_flags_str_}" : '.*'`
  895 + fi
  896 + flags_return=$?
  897 +
  898 + unset _flags_str_
  899 + echo ${flags_output}
  900 + return ${flags_return}
  901 +}
  902 +
  903 +# Use built-in helper function to enable unit testing.
  904 +#
  905 +# Args:
  906 +# None
  907 +# Returns:
  908 +# bool: true if built-ins should be used
  909 +_flags_useBuiltin()
  910 +{
  911 + return ${__FLAGS_USE_BUILTIN}
  912 +}
  913 +
  914 +#------------------------------------------------------------------------------
  915 +# public functions
  916 +#
  917 +# A basic boolean flag. Boolean flags do not take any arguments, and their
  918 +# value is either 1 (false) or 0 (true). For long flags, the false value is
  919 +# specified on the command line by prepending the word 'no'. With short flags,
  920 +# the presense of the flag toggles the current value between true and false.
  921 +# Specifying a short boolean flag twice on the command results in returning the
  922 +# value back to the default value.
  923 +#
  924 +# A default value is required for boolean flags.
  925 +#
  926 +# For example, lets say a Boolean flag was created whose long name was 'update'
  927 +# and whose short name was 'x', and the default value was 'false'. This flag
  928 +# could be explicitly set to 'true' with '--update' or by '-x', and it could be
  929 +# explicitly set to 'false' with '--noupdate'.
  930 +DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; }
  931 +
  932 +# Other basic flags.
  933 +DEFINE_float() { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; }
  934 +DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; }
  935 +DEFINE_string() { _flags_define ${__FLAGS_TYPE_STRING} "$@"; }
  936 +
  937 +# Parse the flags.
  938 +#
  939 +# Args:
  940 +# unnamed: list: command-line flags to parse
  941 +# Returns:
  942 +# integer: success of operation, or error
  943 +FLAGS()
  944 +{
  945 + # define a standard 'help' flag if one isn't already defined
  946 + [ -z "${__flags_help_type:-}" ] && \
  947 + DEFINE_boolean 'help' false 'show this help' 'h'
  948 +
  949 + # parse options
  950 + if [ $# -gt 0 ]; then
  951 + if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then
  952 + _flags_getoptStandard "$@"
  953 + else
  954 + _flags_getoptEnhanced "$@"
  955 + fi
  956 + flags_return=$?
  957 + else
  958 + # nothing passed; won't bother running getopt
  959 + __flags_opts='--'
  960 + flags_return=${FLAGS_TRUE}
  961 + fi
  962 +
  963 + if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then
  964 + _flags_parseGetopt $# "${__flags_opts}"
  965 + flags_return=$?
  966 + fi
  967 +
  968 + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}"
  969 + return ${flags_return}
  970 +}
  971 +
  972 +# This is a helper function for determining the 'getopt' version for platforms
  973 +# where the detection isn't working. It simply outputs debug information that
  974 +# can be included in a bug report.
  975 +#
  976 +# Args:
  977 +# none
  978 +# Output:
  979 +# debug info that can be included in a bug report
  980 +# Returns:
  981 +# nothing
  982 +flags_getoptInfo()
  983 +{
  984 + # platform info
  985 + _flags_debug "uname -a: `uname -a`"
  986 + _flags_debug "PATH: ${PATH}"
  987 +
  988 + # shell info
  989 + if [ -n "${BASH_VERSION:-}" ]; then
  990 + _flags_debug 'shell: bash'
  991 + _flags_debug "BASH_VERSION: ${BASH_VERSION}"
  992 + elif [ -n "${ZSH_VERSION:-}" ]; then
  993 + _flags_debug 'shell: zsh'
  994 + _flags_debug "ZSH_VERSION: ${ZSH_VERSION}"
  995 + fi
  996 +
  997 + # getopt info
  998 + ${FLAGS_GETOPT_CMD} >/dev/null
  999 + _flags_getoptReturn=$?
  1000 + _flags_debug "getopt return: ${_flags_getoptReturn}"
  1001 + _flags_debug "getopt --version: `${FLAGS_GETOPT_CMD} --version 2>&1`"
  1002 +
  1003 + unset _flags_getoptReturn
  1004 +}
  1005 +
  1006 +# Returns whether the detected getopt version is the enhanced version.
  1007 +#
  1008 +# Args:
  1009 +# none
  1010 +# Output:
  1011 +# none
  1012 +# Returns:
  1013 +# bool: true if getopt is the enhanced version
  1014 +flags_getoptIsEnh()
  1015 +{
  1016 + test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH}
  1017 +}
  1018 +
  1019 +# Returns whether the detected getopt version is the standard version.
  1020 +#
  1021 +# Args:
  1022 +# none
  1023 +# Returns:
  1024 +# bool: true if getopt is the standard version
  1025 +flags_getoptIsStd()
  1026 +{
  1027 + test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD}
  1028 +}
  1029 +
  1030 +# This is effectively a 'usage()' function. It prints usage information and
  1031 +# exits the program with ${FLAGS_FALSE} if it is ever found in the command line
  1032 +# arguments. Note this function can be overridden so other apps can define
  1033 +# their own --help flag, replacing this one, if they want.
  1034 +#
  1035 +# Args:
  1036 +# none
  1037 +# Returns:
  1038 +# integer: success of operation (always returns true)
  1039 +flags_help()
  1040 +{
  1041 + if [ -n "${FLAGS_HELP:-}" ]; then
  1042 + echo "${FLAGS_HELP}" >&2
  1043 + else
  1044 + echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2
  1045 + fi
  1046 + if [ -n "${__flags_longNames}" ]; then
  1047 + echo 'flags:' >&2
  1048 + for flags_name_ in ${__flags_longNames}; do
  1049 + flags_flagStr_=''
  1050 + flags_boolStr_=''
  1051 + flags_usName_=`_flags_underscoreName ${flags_name_}`
  1052 +
  1053 + flags_default_=`_flags_getFlagInfo \
  1054 + "${flags_usName_}" ${__FLAGS_INFO_DEFAULT}`
  1055 + flags_help_=`_flags_getFlagInfo \
  1056 + "${flags_usName_}" ${__FLAGS_INFO_HELP}`
  1057 + flags_short_=`_flags_getFlagInfo \
  1058 + "${flags_usName_}" ${__FLAGS_INFO_SHORT}`
  1059 + flags_type_=`_flags_getFlagInfo \
  1060 + "${flags_usName_}" ${__FLAGS_INFO_TYPE}`
  1061 +
  1062 + [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \
  1063 + flags_flagStr_="-${flags_short_}"
  1064 +
  1065 + if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH} ]; then
  1066 + [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \
  1067 + flags_flagStr_="${flags_flagStr_},"
  1068 + # add [no] to long boolean flag names, except the 'help' flag
  1069 + [ ${flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} \
  1070 + -a "${flags_usName_}" != 'help' ] && \
  1071 + flags_boolStr_='[no]'
  1072 + flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:"
  1073 + fi
  1074 +
  1075 + case ${flags_type_} in
  1076 + ${__FLAGS_TYPE_BOOLEAN})
  1077 + if [ ${flags_default_} -eq ${FLAGS_TRUE} ]; then
  1078 + flags_defaultStr_='true'
  1079 + else
  1080 + flags_defaultStr_='false'
  1081 + fi
  1082 + ;;
  1083 + ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER})
  1084 + flags_defaultStr_=${flags_default_} ;;
  1085 + ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;;
  1086 + esac
  1087 + flags_defaultStr_="(default: ${flags_defaultStr_})"
  1088 +
  1089 + flags_helpStr_=" ${flags_flagStr_} ${flags_help_} ${flags_defaultStr_}"
  1090 + _flags_strlen "${flags_helpStr_}" >/dev/null
  1091 + flags_helpStrLen_=${flags_output}
  1092 + flags_columns_=`_flags_columns`
  1093 +
  1094 + if [ ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
  1095 + echo "${flags_helpStr_}" >&2
  1096 + else
  1097 + echo " ${flags_flagStr_} ${flags_help_}" >&2
  1098 + # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06
  1099 + # because it doesn't like empty strings when used in this manner.
  1100 + flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \
  1101 + |awk '{printf "%"length($0)-2"s", ""}'`"
  1102 + flags_helpStr_=" ${flags_emptyStr_} ${flags_defaultStr_}"
  1103 + _flags_strlen "${flags_helpStr_}" >/dev/null
  1104 + flags_helpStrLen_=${flags_output}
  1105 +
  1106 + if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD} \
  1107 + -o ${flags_helpStrLen_} -lt ${flags_columns_} ]; then
  1108 + # indented to match help string
  1109 + echo "${flags_helpStr_}" >&2
  1110 + else
  1111 + # indented four from left to allow for longer defaults as long flag
  1112 + # names might be used too, making things too long
  1113 + echo " ${flags_defaultStr_}" >&2
  1114 + fi
  1115 + fi
  1116 + done
  1117 + fi
  1118 +
  1119 + unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \
  1120 + flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \
  1121 + flags_columns_ flags_short_ flags_type_ flags_usName_
  1122 + return ${FLAGS_TRUE}
  1123 +}
  1124 +
  1125 +# Reset shflags back to an uninitialized state.
  1126 +#
  1127 +# Args:
  1128 +# none
  1129 +# Returns:
  1130 +# nothing
  1131 +flags_reset()
  1132 +{
  1133 + for flags_name_ in ${__flags_longNames}; do
  1134 + flags_usName_=`_flags_underscoreName ${flags_name_}`
  1135 + flags_strToEval_="unset FLAGS_${flags_usName_}"
  1136 + for flags_type_ in \
  1137 + ${__FLAGS_INFO_DEFAULT} \
  1138 + ${__FLAGS_INFO_HELP} \
  1139 + ${__FLAGS_INFO_SHORT} \
  1140 + ${__FLAGS_INFO_TYPE}
  1141 + do
  1142 + flags_strToEval_=\
  1143 +"${flags_strToEval_} __flags_${flags_usName_}_${flags_type_}"
  1144 + done
  1145 + eval ${flags_strToEval_}
  1146 + done
  1147 +
  1148 + # reset internal variables
  1149 + __flags_boolNames=' '
  1150 + __flags_longNames=' '
  1151 + __flags_shortNames=' '
  1152 + __flags_definedNames=' '
  1153 +
  1154 + unset flags_name_ flags_type_ flags_strToEval_ flags_usName_
  1155 +}
... ...
tools/lib/utilities.sh 0 → 100755
  1 +#!/usr/bin/env bash
  2 +
  3 +# Any subsequent commands which fail will cause the shell script to exit immediately
  4 +# set -e
  5 +
  6 +# ANSI escape codes for message colouring
  7 +RED='\033[0;31m'
  8 +GREEN='\033[0;32m'
  9 +LIGHT_GREEN='\033[1;32m'
  10 +NC='\033[0m' # No Color
  11 +
  12 +printInfo () {
  13 + echo -e "${LIGHT_GREEN}${1}${NC}"
  14 +}
  15 +export -f printInfo
  16 +
  17 +printError () {
  18 + echo -e "${RED}${1}${NC}"
  19 +}
  20 +export -f printError
0 21 \ No newline at end of file
... ...