blob: 66cbc3ce2f013812744a74fbff86acf88bd7c6eb [file] [log] [blame]
/************************************************************************
*
* braceexp.cpp - definitions of rw_brace_expand and rw_shell_expand
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
**************************************************************************/
// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC
#include <stdlib.h> // for malloc(), free()
#include <string.h> // for memcpy()
#include <ctype.h> // for isspace()
#include <assert.h> // for assert()
#include <rw_braceexp.h>
// for convenience
typedef unsigned char UChar;
// search `beg' to `end' for a character that `fn'
// returns non-zero.
static const char*
_rw_find_match (const char* beg,
const char* end,
bool match_space)
{
bool is_escaped = false;
for (/**/; beg < end; ++beg) {
const bool is_space = 0 != isspace (UChar (*beg));
if (!is_escaped && match_space == is_space) {
return beg;
}
is_escaped = !is_escaped && (*beg == '\\');
}
return 0;
}
// similar to sprintf (buffer, "%ld", value), but the output
// is not locale dependent. we choose not to use code from
// the test harness because we may need to move this around.
static int _rw_itoa_10 (char* buffer, long value)
{
const bool neg = value < 0;
if (neg)
value = 0 - value;
// write it out in reverse
int n = 0;
do {
const long q = value / 10;
const long r = value - (q * 10);
value = q;
switch (r)
{
case 0: buffer [n] = '0'; break;
case 1: buffer [n] = '1'; break;
case 2: buffer [n] = '2'; break;
case 3: buffer [n] = '3'; break;
case 4: buffer [n] = '4'; break;
case 5: buffer [n] = '5'; break;
case 6: buffer [n] = '6'; break;
case 7: buffer [n] = '7'; break;
case 8: buffer [n] = '8'; break;
case 9: buffer [n] = '9'; break;
default: break;
}
n += 1;
} while (value != 0);
// write the sign
if (neg)
buffer [n++] = '-';
// then reverse it
for (int i = 0, j = n - 1; i < j; ++i, --j)
{
const char c = buffer [i];
buffer [i] = buffer [j];
buffer [j] = c;
}
return n;
}
// search `beg' to `end' for the next unescaped open brace . if `end'
// is 0 then the search will terminate at the end of string. returns 0
// on failure.
//
// this function ignores escaped braces and those that look like they
// belong to a shell variable expansion. this is for consistency with
// bash. i.e. there is no open brace in a\{b\}c or a${b}c.
static const char* _rw_find_open_brace (const char* beg,
const char* end)
{
bool is_escaped = false;
bool is_shelled = false;
for (/**/; beg < end; ++beg) {
const bool is_match = (*beg == '{');
if (!is_escaped && !is_shelled && is_match) {
return beg;
}
is_shelled = !is_shelled && (*beg == '$');
is_escaped = !is_escaped && (*beg == '\\');
}
return 0;
}
// search `beg' to `end' for the next close brace at the current brace
// depth. if `end' is 0, the search will continue until a match or end
// of string. returns 0 on failure.
static const char* _rw_find_close_brace (const char* beg,
const char* end)
{
bool is_escaped = false;
// nested brace depth
for (int depth = 1; beg < end; ++beg) {
const bool is_open_brace = (*beg == '{');
if (!is_escaped && is_open_brace) {
++depth;
}
const bool is_close_brace = (*beg == '}');
if (!is_escaped && is_close_brace) {
--depth;
}
is_escaped = !is_escaped && (*beg == '\\');
if (depth == 0)
return beg;
}
return 0;
}
// search `beg' to `end' for the next unescaped comma at the current
// brace depth. if `end' is 0, the search will continue until a match
// or end of string. returns 0 on failure.
static const char* _rw_find_next_comma (const char* beg,
const char* end)
{
bool is_escaped = false;
// nested brace depth
for (int depth = 0; beg < end; ++beg) {
const bool is_open_brace = (*beg == '{');
if (!is_escaped && is_open_brace) {
++depth;
}
const bool is_close_brace = (*beg == '}');
if (!is_escaped && is_close_brace) {
--depth;
}
const bool is_comma = (*beg == ',');
if (!is_escaped && is_comma) {
if (depth == 0)
return beg;
}
is_escaped = !is_escaped && (*beg == '\\');
}
return 0;
}
struct _rw_string_buffer
{
_rw_string_buffer (char* s, size_t n)
: capacity_ (n)
, length_ (0)
, buffer_ (s)
, owned_ (false)
{
}
// destructor does not deallocate memory
// user expected to do that
size_t capacity_;
size_t length_;
char* buffer_;
bool owned_;
bool append (const char* s, size_t n);
private:
// not implemented
_rw_string_buffer (const _rw_string_buffer&);
_rw_string_buffer& operator= (const _rw_string_buffer&);
};
// this is where most of the work is done.
struct _rw_brace_graph
{
//
_rw_brace_graph ();
//
~_rw_brace_graph ();
// expand brace expression from `beg' to `end' into `len' bytes of
// `buf'. if it won't fit, we allocate a buffer with malloc() and
// return that. so the caller needs to free() the return buffer if
// it is not equal to `buf'.
char* build_and_expand (const char* beg,
const char* end,
char* buf, size_t len, char sep);
private:
// not implemented
_rw_brace_graph (const _rw_brace_graph&);
_rw_brace_graph& operator= (const _rw_brace_graph&);
// node for a directed-acyclic-graph that we build from the original
// brace expression
struct _rw_brace_node
{
const char* str_;
size_t len_;
_rw_brace_node* sibling_;
_rw_brace_node* child_;
};
// retrieve a new node. nodes are allocated in large blocks. those
// blocks are deallocated when this graph instance is destroyed.
// and they are reused for every build_and_expand() call.
_rw_brace_node* get_new_node ();
// this function generates a dag from an arbitrary brace expression.
// the format is `prefix{body}suffix'. prefix, and suffix can both
// be the empty string. body may be a comma seperated list of tokens,
// a sequence of tokens, or arbitrary literal text. escapes on commas
// and braces are ignored, and left intact otherwise.
_rw_brace_node* build_anything (const char* beg, const char* end);
// generates a dag from an arbitrary sequence brace expression. the
// format is `{?..?}suffix' where both ? are alphabetic characters
// of the same character class [upper/lower].
_rw_brace_node* build_character_sequence (const char* beg,
const char* end);
// generates a dag from an arbitrary sequence brace expression. the
// format is `{?..?}suffix' where both ? are integer expressions.
_rw_brace_node* build_integer_sequence (const char* beg,
const char* end);
// generates a dag from an arbitrary list brace expression. the
// format is `{a,b[,c]}suffix', where `a', `b' and `c' are full
// brace expansions that would be processed by build_anything.
_rw_brace_node* build_list (const char* beg, const char* end);
// the number of nodes held by each brace buffer [see below]
enum { size = 64 };
#ifdef _RWSTD_NO_NESTED_CLASS_ACCESS
// allow _rw_brace_node_buffer access to _rw_brace_graph's private
// type(s) if the resolution of cwg issue 45 is not yet implemented
struct _rw_brace_node_buffer;
friend struct _rw_brace_node_buffer;
// allow _rw_recursion_context access to _rw_brace_graph's private
// type(s) if the resolution of cwg issue 45 is not yet implemented
struct _rw_recursion_context;
friend struct _rw_recursion_context;
#endif // _RWSTD_NO_NESTED_CLASS_ACCESS
// this is essentially a rope with a fixed length payload of
// brace nodes
struct _rw_brace_node_buffer
{
_rw_brace_node nodes_ [size];
size_t used_; // number of nodes_ used in this buffer
_rw_brace_node_buffer* next_;
};
// the initial set of nodes is preallocated as part of this graph
// object instance. additional blocks of nodes will be allocated
// as is necessary by the get_new_node() member.
_rw_brace_node_buffer node_cache_;
_rw_brace_node_buffer* nodes_; // pointer to last allocated node buffer
// code for handling integer ranges
void reset_for_reuse (char* buf, size_t len);
void free_range_buffers ();
// this is essentially a rope with a variable length payload
struct _rw_range_buffer
{
_rw_range_buffer* next_;
};
_rw_range_buffer* ranges_;
// code for generating the string
_rw_string_buffer string_;
// we use this to walk the stack. we need to walk from the root
// of the tree to each leaf. when we reach a leaf, we need a way
// to see the path that we took to get where we are. we use this
// path to write out each part of the full expansion.
struct _rw_recursion_context
{
_rw_recursion_context (_rw_brace_node* pnode)
: parent_ (0), node_ (pnode)
{
}
_rw_recursion_context (_rw_recursion_context* parent)
: parent_ (parent), node_ (parent->node_->child_)
{
}
_rw_recursion_context* parent_;
_rw_brace_node* node_;
};
// this function walks the dag, leaving a trail of context
// objects so we can walk back to the root and write the data
// at each level in the graph.
bool brace_expand (_rw_recursion_context* self, char sep);
// this function writes the data at each level of the dag
// to our internal buffer.
bool brace_expand_write (_rw_recursion_context* context);
};
_rw_brace_graph::_rw_brace_graph ()
: nodes_ (&node_cache_)
, ranges_ (0)
, string_ (0, 0)
{
node_cache_.next_ = 0;
}
_rw_brace_graph::~_rw_brace_graph ()
{
_rw_brace_node_buffer* nodes = node_cache_.next_;
while (nodes) {
_rw_brace_node_buffer* next = nodes->next_;
nodes->next_ = 0;
// deallocate this buffer
free (nodes);
// setup to deallocate next one
nodes = next;
}
reset_for_reuse (0, 0);
}
char*
_rw_brace_graph::build_and_expand (const char* beg,
const char* end,
char* buf, size_t len, char sep)
{
assert (beg != 0);
assert (end != 0);
if (!beg || !end)
return 0;
// reset member data so we can reuse allocated buffers
// if there are any
reset_for_reuse (buf, len);
_rw_brace_node* root
= build_anything (beg, end);
if (!root)
return 0;
// this helps us do the building of the output string
_rw_recursion_context context (root);
if (!brace_expand (&context, sep)) {
if (string_.owned_)
free (string_.buffer_);
string_.buffer_ = 0;
}
// kill the last seperator with a null terminator
else if (string_.buffer_) {
const size_t pos = string_.length_ < 1 ? 0 : string_.length_ - 1;
string_.buffer_ [pos] = '\0';
}
return string_.buffer_;
}
_rw_brace_graph::_rw_brace_node*
_rw_brace_graph::get_new_node ()
{
nodes_->used_ += 1;
// if we run out of space, move to the next buffer
if (! (nodes_->used_ < size)) {
// if we have got a buffer, reuse it
if (nodes_->next_) {
nodes_ = nodes_->next_;
}
// otherwise we allocate one
else {
const size_t sz = sizeof (_rw_brace_node_buffer);
nodes_->next_ = (_rw_brace_node_buffer*)malloc (sz);
if (!nodes_->next_)
return 0;
nodes_ = nodes_->next_;
nodes_->next_ = 0;
}
nodes_->used_ = 1;
}
_rw_brace_node* node = &nodes_->nodes_ [nodes_->used_ - 1];
node->str_ = 0;
node->len_ = 0;
node->sibling_ = 0;
node->child_ = 0;
return node;
}
_rw_brace_graph::_rw_brace_node*
_rw_brace_graph::build_anything (const char* beg, const char* end)
{
//
const char* open_brace = _rw_find_open_brace (beg, end);
if (open_brace) {
// build a node for the prefix if there is one
_rw_brace_node* prefix = get_new_node ();
if (!prefix)
return 0;
prefix->str_ = beg;
prefix->len_ = (open_brace - beg);
_rw_brace_node* child = 0;
// try to build a character sequence expansion like {a..g}
child = build_character_sequence (open_brace, end);
if (!child) {
// try to do an integer sequence expansion like {-19..+72}
child = build_integer_sequence (open_brace, end);
if (!child) {
// try to do a list expansion like {a,b,cd}
child = build_list (open_brace, end);
if (!child) {
// we can't figure out what to do, so we fail
return 0;
}
}
}
// we must have a valid child pointer at this point
prefix->child_ = child;
return prefix;
}
// there was no open brace, so the entire text from beg to end
// is a literal
_rw_brace_node* prefix = get_new_node ();
if (!prefix)
return 0;
prefix->str_ = beg;
prefix->len_ = beg < end ? (end - beg) : 0;
return prefix;
}
_rw_brace_graph::_rw_brace_node*
_rw_brace_graph::build_integer_sequence (const char* beg, const char* end)
{
// check for {-10..100} type sequence expression. make sure not to
// reference past the end of the string.
if (*beg++ != '{')
return 0;
// this should point to first character after the integer value
char* pend;
// parse the first integer
long ibeg = strtol (beg, &pend, 10);
if (!ibeg && (pend == beg))
return 0; // failed to parse an integer value
// number of characters needed to represent ibeg
const size_t ibeg_dig = (pend - beg);
// make sure we have two dots
beg = pend;
if (! (beg [0] == '.' && beg [1] == '.'))
return 0;
// skip the two dots
beg += 2;
// parse the second integer
long iend = strtol (beg, &pend, 10);
if (!iend && (pend == beg))
return 0; // failed to parse an integer value
// number of characters needed to represent iend
const size_t iend_dig = (pend - beg);
// make sure we have an end brace
beg = pend;
if (beg [0] != '}')
return 0;
beg += 1;
// build the suffix
_rw_brace_node* suffix = build_anything (beg, end);
if (!suffix)
return 0; // failed to parse suffix
// direction the range goes, +1 for increasing, -1 for decreasing
const int dir = (ibeg < iend) ? 1 : -1;
// maximum length of the string representation of a single
// integer in the range
const size_t len = (ibeg_dig < iend_dig ? iend_dig : ibeg_dig);
// number of integers in the range [ibeg, iend]
const size_t num = 1 + (iend - ibeg) * dir;
// maximum number of bytes needed to represent all of the numbers
// and a single null
const size_t cnt = 1 + (num * len);
// number of bytes we have to allocate, cnt of which is data
// and the rest is to allow us to chain these buffers together
const size_t bsz = cnt + sizeof (_rw_range_buffer);
// allocate a rope segment big enough for all of the strings
// we need to keep.
_rw_range_buffer* buffer
= (_rw_range_buffer*)malloc (bsz);
if (!buffer)
return 0;
// add buffer to our list so we can free it later
buffer->next_ = ranges_;
ranges_ = buffer;
// pointer to the current write position in the buffer
char* ranges = (char*)&buffer [1];
_rw_brace_node* first_child = 0;
_rw_brace_node* final_child = 0;
// build a list of children, associate each with suffix
for (/**/; ibeg != iend; ibeg += dir) {
// create a child from whatever is between beg and end. that childs
// next pointer will be the suffix we created above.
_rw_brace_node* child = get_new_node ();
if (!child)
return 0;
// add a representation of cbeg to the ranges buffer
child->len_ = _rw_itoa_10 (ranges, ibeg);
child->str_ = ranges;
// step past number of written characters
ranges += child->len_;
// track children we have created
if (!first_child)
first_child = child;
if (final_child)
final_child->sibling_ = child;
final_child = child;
// suffix is the suffix of the child expression
while (child->child_)
child = child->child_;
child->child_ = suffix;
}
// create the last node in the sequence.
_rw_brace_node* child = get_new_node ();
if (!child)
return 0;
// add a representation of cbeg to the ranges buffer
child->len_ = _rw_itoa_10 (ranges, ibeg);
child->str_ = ranges;
// trach child
if (!first_child)
first_child = child;
if (final_child)
final_child->sibling_ = child;
final_child = child;
// suffix is the suffix of the child expression
while (child->child_)
child = child->child_;
child->child_ = suffix;
return first_child;
}
_rw_brace_graph::_rw_brace_node*
_rw_brace_graph::build_character_sequence (const char* beg, const char* end)
{
// check for {a..z} type sequence expression. make sure not to
// reference past the end of the string.
if ( beg [0] != '{'
|| beg [1] == '\0'
|| beg [2] != '.'
|| beg [3] != '.'
|| beg [4] == '\0'
|| beg [5] != '}')
return 0;
// grab characters that represent the start and end of the sequence
char cbeg = beg [1];
char cend = beg [4];
// only works if sequence characters are both lowercase or uppercase.
const int both_are_lower =
islower (UChar (cbeg)) && islower (UChar (cend));
const int both_are_upper =
isupper (UChar (cbeg)) && isupper (UChar (cend));
if (! (both_are_lower || both_are_upper))
return 0;
// these must live for duration of program
static const char* alpha_table [] =
{
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
};
const char* sequence = alpha_table [both_are_upper];
const int dir = (cbeg < cend) ? 1 : -1;
// build the suffix
_rw_brace_node* suffix = build_anything (beg + 6, end);
if (!suffix)
return 0; // failed to parse suffix
_rw_brace_node* first_child = 0;
_rw_brace_node* final_child = 0;
// build a list of children, associate each with suffix
for (/**/; cbeg != cend; cbeg += dir) {
// create a child from whatever is between beg and end. that childs
// next pointer will be the suffix we created above.
_rw_brace_node* child = get_new_node ();
if (!child)
return 0;
// this finds beg in our array, we could have used strchr
child->str_ = sequence + (cbeg - sequence [0]);
child->len_ = 1;
// track children we have created
if (!first_child)
first_child = child;
if (final_child)
final_child->sibling_ = child;
final_child = child;
// suffix is the suffix of the child expression
while (child->child_)
child = child->child_;
child->child_ = suffix;
}
// create the last node in the sequence.
_rw_brace_node* child = get_new_node ();
if (!child)
return 0;
child->str_ = sequence + (cbeg - sequence [0]);
child->len_ = 1;
// trach child
if (!first_child)
first_child = child;
if (final_child)
final_child->sibling_ = child;
final_child = child;
// suffix is the suffix of the child expression
while (child->child_)
child = child->child_;
child->child_ = suffix;
return first_child;
}
_rw_brace_graph::_rw_brace_node*
_rw_brace_graph::build_list (const char* beg, const char* end)
{
// we really expect that the first token is an open paren the
// caller should have consumed the prefix before calling this
if (*beg++ != '{')
return false;
// now fill in the middle, each child we create directly will
// have a child pointer to the suffix node
// special case {}?
// find the end of the brace list
const char* end_of_list = _rw_find_close_brace (beg, end);
if (!end_of_list)
return false; // no list terminator
// build a node for the suffix.
_rw_brace_node* suffix = build_anything (end_of_list + 1, end);
if (!suffix)
return false; // failed to parse end
// find the end of the first comma seperated token
const char* mid = _rw_find_next_comma (beg, end_of_list);
_rw_brace_node* first_child = 0;
_rw_brace_node* final_child = 0;
while (mid) {
// create a child from whatever is between beg and end. that childs
// next pointer will be the suffix we created above.
_rw_brace_node* child = build_anything (beg, mid);
if (!child)
return false;
if (!first_child)
first_child = child;
// append new child to end of chain
if (final_child)
final_child->sibling_ = child;
final_child = child;
// the nex pointer for this child is the suffix
// suffix is the suffix of the child expression
while (child->child_)
child = child->child_;
child->child_ = suffix;
beg = mid + 1;
mid = _rw_find_next_comma (beg, end_of_list);
}
// okay, we have a pointer to the last comma in the list. create an
// item for the data between the comma and the list terminator
// '{abc,d{1..3}e}a'
// ^ ^ ^
// beg eol end
// build nodes from the last entry in the list
_rw_brace_node* child = build_anything (beg, end_of_list);
if (!child)
return false;
if (!first_child)
first_child = child;
if (final_child)
final_child->sibling_ = child;
final_child = child;
while (child->child_)
child = child->child_;
child->child_ = suffix;
// success, return first child in this expansion
return first_child;
}
void _rw_brace_graph::reset_for_reuse (char* buf, size_t len)
{
node_cache_.used_ = 0;
nodes_ = &node_cache_;
string_.buffer_ = buf;
string_.capacity_ = len;
string_.length_ = 0;
// we free these buffers because the size of the buffer
// depends on the expression we are evaluating
free_range_buffers ();
}
void _rw_brace_graph::free_range_buffers ()
{
_rw_range_buffer* ranges = ranges_;
while (ranges) {
_rw_range_buffer* next = ranges->next_;
ranges->next_ = 0;
free (ranges);
ranges = next;
}
ranges_ = 0;
}
bool _rw_brace_graph::brace_expand_write (_rw_recursion_context* context)
{
if (context->parent_) {
if (!brace_expand_write (context->parent_))
return false;
}
bool is_escaped = false;
const char* beg = context->node_->str_;
const size_t len = context->node_->len_;
for (size_t n = 0; n < len; ++n, ++beg) {
is_escaped = !is_escaped && (*beg == '\\');
if (!is_escaped) {
if (!string_.append (beg, 1))
return false;
}
}
return true;
}
bool _rw_brace_graph::brace_expand (_rw_recursion_context* self, char sep)
{
// if this node has no children or siblings, we have found a full
// expansion.
if (!self->node_ ||
!self->node_->sibling_ && !self->node_->child_) {
const size_t length_before = string_.length_;
// use recursion again to walk back to the root the graph and
// write each contexts data as we unwind back toward the leaf
if (!brace_expand_write (self))
return false;
const size_t length_after = string_.length_;
// don't write a seperator if we wrote no data
if (length_before != length_after && !string_.append (&sep, 1))
return false;
return true;
}
// iterate through all of the children of the node, thus finding all
// other combinations
_rw_recursion_context context (self);
while (context.node_) {
if (!brace_expand (&context, sep))
return false;
context.node_ = context.node_->sibling_;
}
return true;
}
bool _rw_string_buffer::append (const char* s, size_t n)
{
const size_t new_len = length_ + n;
// not enough space, grow buf
if (! (new_len < capacity_)) {
// buf grows in 256 byte blocks
size_t new_cap = capacity_;
while (! (new_len < new_cap))
new_cap += 256;
char* new_buf = (char*)malloc (new_cap);
if (!new_buf)
return false;
// copy the old data
memcpy (new_buf, buffer_, length_);
// copy the new data
memcpy (new_buf + length_, s, n);
new_buf [new_len] = '\0';
// don't free until after append because `s' may
// be in string_buf_
if (owned_)
free (buffer_);
capacity_ = new_cap;
length_ = new_len;
buffer_ = new_buf;
owned_ = true;
}
// just copy the data
else {
memcpy (buffer_ + length_, s, n);
buffer_ [new_len] = '\0';
length_ = new_len;
}
return true;
}
//
char* rw_brace_expand (const char* brace_expr,
size_t sz,
char* s, size_t n, char sep)
{
if (!brace_expr)
return 0;
// if the length isn't provided, assume entire string
if (!sz)
sz = strlen (brace_expr);
_rw_brace_graph graph;
// build the graph, and then expand it into buf
char* buf = graph.build_and_expand (brace_expr,
brace_expr + sz, s, n, sep);
if (!buf)
return 0;
return buf;
}
//
char* rw_shell_expand (const char* shell_expr, size_t sz,
char* s, size_t n, char sep)
{
if (!shell_expr)
return 0;
// if the length isn't provided, assume entire string
if (!sz)
sz = strlen (shell_expr);
// assume shell_expr is null terminated
const char* beg = shell_expr;
const char* end = shell_expr + sz;
// helper for output
_rw_string_buffer result (s, n);
// helper for expanding braces
_rw_brace_graph graph;
// first non-whitespace character
const char* tok_beg = _rw_find_match (beg, end, false);
if (!tok_beg) {
if (!result.append ("\0", 1))
return 0;
return result.buffer_;
}
bool is_first_expand = true;
while (tok_beg)
{
// first whitespace character
const char* tok_end = _rw_find_match (tok_beg, end, true);
if (!tok_end)
tok_end = end;
// expand from tok_beg to tok_end into buf
char buf [256];
char* exp =
graph.build_and_expand (tok_beg, tok_end, buf, sizeof (buf), sep);
// apptok_end space, then expansion, expansion
bool app = false;
if (exp) {
// in case user uses a null seperator
const char* term = exp;
for (/**/; *term; term += strlen (term) + 1)
;
const size_t len = exp < term ? (term - exp) - 1 : 0;
if (is_first_expand)
app = result.append (exp, len);
else if (result.append (&sep, 1))
app = result.append (exp, len);
}
is_first_expand = false;
if (exp != buf)
free (exp);
// if we didn't append, or we failed to append
// then this function fails
if (!app) {
if (result.owned_)
free (result.buffer_);
return 0;
}
// bail if we're at the end of string
if (!tok_end)
break;
// fid next nonwhitespace, which is the start of the next token
tok_beg = _rw_find_match (tok_end, end, false);
}
return result.buffer_;
}