head 1.107; access; symbols LMTP2NNTP_1_4_1:1.107 LMTP2NNTP_1_4_0:1.107 VAR_1_1_3:1.107 VAR_1_1_2:1.105 VAR_1_1_1:1.104 LMTP2NNTP_1_3_0:1.104 LMTP2NNTP_1_3b2:1.104 LMTP2NNTP_1_3b1:1.104 LMTP2NNTP_1_3a3:1.104 LMTP2NNTP_1_3a2:1.104 LMTP2NNTP_1_3a1:1.104 VAR_1_1_0:1.104 VAR_1_0_0:1.99 LMTP2NNTP_1_2_0:1.96 LMTP2NNTP_1_2b4:1.96 LMTP2NNTP_1_2b3:1.96 LMTP2NNTP_1_2b2:1.96 LMTP2NNTP_1_2b1:1.90 LMTP2NNTP_1_2a8:1.90 LMTP2NNTP_1_2a7:1.90 LMTP2NNTP_1_2a6:1.90 LMTP2NNTP_1_2a5:1.89 LMTP2NNTP_1_2a4:1.89 VAR_0_9_0:1.89 LMTP2NNTP_1_2a3:1.78 CODE_CLEANUP_AFTER:1.75 CODE_CLEANUP_BEFORE:1.69 OSSP_RC_SPEC:1.60; locks; strict; comment @ * @; 1.107 date 2005.03.29.19.31.49; author rse; state Exp; branches; next 1.106; 1.106 date 2005.01.20.20.29.06; author rse; state Exp; branches; next 1.105; 1.105 date 2004.10.29.19.42.20; author rse; state Exp; branches; next 1.104; 1.104 date 2004.04.04.09.57.10; author rse; state Exp; branches; next 1.103; 1.103 date 2004.04.04.08.07.34; author rse; state Exp; branches; next 1.102; 1.102 date 2004.04.04.08.04.34; author rse; state Exp; branches; next 1.101; 1.101 date 2004.02.17.09.21.06; author thl; state Exp; branches; next 1.100; 1.100 date 2003.07.31.07.29.52; author thl; state Exp; branches; next 1.99; 1.99 date 2003.02.14.21.17.07; author rse; state Exp; branches; next 1.98; 1.98 date 2003.02.14.19.46.21; author rse; state Exp; branches; next 1.97; 1.97 date 2003.02.14.18.19.28; author rse; state Exp; branches; next 1.96; 1.96 date 2003.02.10.21.15.41; author rse; state Exp; branches; next 1.95; 1.95 date 2003.02.10.21.12.47; author rse; state Exp; branches; next 1.94; 1.94 date 2003.02.10.20.50.53; author rse; state Exp; branches; next 1.93; 1.93 date 2003.02.10.20.38.18; author rse; state Exp; branches; next 1.92; 1.92 date 2003.02.10.20.21.49; author rse; state Exp; branches; next 1.91; 1.91 date 2003.02.10.15.37.15; author rse; state Exp; branches; next 1.90; 1.90 date 2002.07.05.20.17.16; author rse; state Exp; branches; next 1.89; 1.89 date 2002.03.08.12.55.52; author rse; state Exp; branches; next 1.88; 1.88 date 2002.03.08.12.37.52; author rse; state Exp; branches; next 1.87; 1.87 date 2002.03.07.14.03.50; author rse; state Exp; branches; next 1.86; 1.86 date 2002.03.07.12.11.09; author rse; state Exp; branches; next 1.85; 1.85 date 2002.03.07.09.14.05; author rse; state Exp; branches; next 1.84; 1.84 date 2002.03.07.09.08.11; author rse; state Exp; branches; next 1.83; 1.83 date 2002.03.06.11.45.03; author rse; state Exp; branches; next 1.82; 1.82 date 2002.03.06.10.33.15; author rse; state Exp; branches; next 1.81; 1.81 date 2002.03.06.10.18.19; author rse; state Exp; branches; next 1.80; 1.80 date 2002.03.06.10.05.10; author rse; state Exp; branches; next 1.79; 1.79 date 2002.03.06.10.04.29; author rse; state Exp; branches; next 1.78; 1.78 date 2002.03.04.13.12.23; author rse; state Exp; branches; next 1.77; 1.77 date 2002.03.04.12.01.53; author rse; state Exp; branches; next 1.76; 1.76 date 2002.03.04.11.53.27; author rse; state Exp; branches; next 1.75; 1.75 date 2002.03.04.11.37.34; author rse; state Exp; branches; next 1.74; 1.74 date 2002.03.02.12.29.05; author rse; state Exp; branches; next 1.73; 1.73 date 2002.03.01.22.02.40; author rse; state Exp; branches; next 1.72; 1.72 date 2002.03.01.21.59.37; author rse; state Exp; branches; next 1.71; 1.71 date 2002.03.01.20.28.33; author rse; state Exp; branches; next 1.70; 1.70 date 2002.02.28.20.28.14; author rse; state Exp; branches; next 1.69; 1.69 date 2002.02.28.12.40.01; author thl; state Exp; branches; next 1.68; 1.68 date 2002.02.28.12.10.33; author rse; state Exp; branches; next 1.67; 1.67 date 2002.02.28.11.59.29; author rse; state Exp; branches; next 1.66; 1.66 date 2002.02.28.11.54.25; author rse; state Exp; branches; next 1.65; 1.65 date 2002.02.28.11.08.58; author rse; state Exp; branches; next 1.64; 1.64 date 2002.02.28.09.29.48; author rse; state Exp; branches; next 1.63; 1.63 date 2002.02.28.08.48.44; author rse; state Exp; branches; next 1.62; 1.62 date 2002.02.28.08.08.16; author rse; state Exp; branches; next 1.61; 1.61 date 2002.02.27.13.44.16; author rse; state Exp; branches; next 1.60; 1.60 date 2002.01.30.19.37.14; author rse; state Exp; branches; next 1.59; 1.59 date 2002.01.09.11.27.07; author rse; state Exp; branches; next 1.58; 1.58 date 2002.01.02.17.12.18; author rse; state Exp; branches; next 1.57; 1.57 date 2001.12.17.12.57.46; author rse; state Exp; branches; next 1.56; 1.56 date 2001.12.17.10.29.43; author rse; state Exp; branches; next 1.55; 1.55 date 2001.12.17.09.03.58; author rse; state Exp; branches; next 1.54; 1.54 date 2001.12.17.09.00.29; author rse; state Exp; branches; next 1.53; 1.53 date 2001.12.16.23.40.16; author rse; state Exp; branches; next 1.52; 1.52 date 2001.12.14.14.40.28; author rse; state Exp; branches; next 1.51; 1.51 date 2001.12.14.14.06.50; author rse; state Exp; branches; next 1.50; 1.50 date 2001.12.14.13.47.01; author rse; state Exp; branches; next 1.49; 1.49 date 2001.12.14.13.42.56; author rse; state Exp; branches; next 1.48; 1.48 date 2001.12.13.16.25.58; author simons; state Exp; branches; next 1.47; 1.47 date 2001.12.13.16.03.42; author simons; state Exp; branches; next 1.46; 1.46 date 2001.12.13.14.34.17; author simons; state Exp; branches; next 1.45; 1.45 date 2001.12.12.17.18.55; author simons; state Exp; branches; next 1.44; 1.44 date 2001.12.12.16.52.56; author simons; state Exp; branches; next 1.43; 1.43 date 2001.12.12.16.51.20; author simons; state Exp; branches; next 1.42; 1.42 date 2001.12.08.16.27.57; author simons; state Exp; branches; next 1.41; 1.41 date 2001.12.08.16.17.56; author simons; state Exp; branches; next 1.40; 1.40 date 2001.12.08.16.02.15; author simons; state Exp; branches; next 1.39; 1.39 date 2001.12.04.14.25.06; author simons; state Exp; branches; next 1.38; 1.38 date 2001.12.04.13.36.37; author simons; state Exp; branches; next 1.37; 1.37 date 2001.12.03.10.51.27; author simons; state Exp; branches; next 1.36; 1.36 date 2001.11.20.15.46.35; author simons; state Exp; branches; next 1.35; 1.35 date 2001.11.20.15.36.42; author simons; state Exp; branches; next 1.34; 1.34 date 2001.11.20.14.04.06; author thl; state Exp; branches; next 1.33; 1.33 date 2001.11.20.13.44.01; author thl; state Exp; branches; next 1.32; 1.32 date 2001.11.20.13.33.53; author thl; state Exp; branches; next 1.31; 1.31 date 2001.11.20.12.45.10; author thl; state Exp; branches; next 1.30; 1.30 date 2001.11.20.12.24.53; author thl; state Exp; branches; next 1.29; 1.29 date 2001.11.20.12.22.42; author thl; state Exp; branches; next 1.28; 1.28 date 2001.11.20.11.46.34; author simons; state Exp; branches; next 1.27; 1.27 date 2001.11.20.11.44.12; author simons; state Exp; branches; next 1.26; 1.26 date 2001.11.19.16.30.18; author thl; state Exp; branches; next 1.25; 1.25 date 2001.11.19.16.07.15; author simons; state Exp; branches; next 1.24; 1.24 date 2001.11.19.16.05.28; author thl; state Exp; branches; next 1.23; 1.23 date 2001.11.19.15.56.17; author thl; state Exp; branches; next 1.22; 1.22 date 2001.11.19.15.15.31; author simons; state Exp; branches; next 1.21; 1.21 date 2001.11.19.14.10.48; author simons; state Exp; branches; next 1.20; 1.20 date 2001.11.16.16.01.20; author simons; state Exp; branches; next 1.19; 1.19 date 2001.11.15.15.36.05; author simons; state Exp; branches; next 1.18; 1.18 date 2001.11.14.12.57.56; author rse; state Exp; branches; next 1.17; 1.17 date 2001.11.14.12.52.58; author rse; state Exp; branches; next 1.16; 1.16 date 2001.11.14.12.21.05; author rse; state Exp; branches; next 1.15; 1.15 date 2001.11.14.12.02.40; author rse; state Exp; branches; next 1.14; 1.14 date 2001.11.14.11.11.01; author rse; state Exp; branches; next 1.13; 1.13 date 2001.11.14.09.47.02; author rse; state Exp; branches; next 1.12; 1.12 date 2001.11.13.19.32.03; author rse; state Exp; branches; next 1.11; 1.11 date 2001.11.13.19.28.56; author rse; state Exp; branches; next 1.10; 1.10 date 2001.11.13.19.25.41; author rse; state Exp; branches; next 1.9; 1.9 date 2001.11.13.14.56.05; author simons; state Exp; branches; next 1.8; 1.8 date 2001.11.13.14.42.57; author simons; state Exp; branches; next 1.7; 1.7 date 2001.11.13.14.36.55; author simons; state Exp; branches; next 1.6; 1.6 date 2001.11.13.13.32.58; author simons; state Exp; branches; next 1.5; 1.5 date 2001.11.13.13.29.50; author simons; state Exp; branches; next 1.4; 1.4 date 2001.11.13.13.25.18; author simons; state Exp; branches; next 1.3; 1.3 date 2001.11.13.12.45.49; author simons; state Exp; branches; next 1.2; 1.2 date 2001.11.13.12.16.15; author simons; state Exp; branches; next 1.1; 1.1 date 2001.11.13.12.08.30; author simons; state Exp; branches; next ; desc @@ 1.107 log @annotate with lint message @ text @/* ** OSSP var - Variable Expansion ** Copyright (c) 2001-2005 Ralf S. Engelschall ** Copyright (c) 2001-2005 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2005 Cable & Wireless (http://www.cw.com/) ** ** This file is part of OSSP var, a variable expansion ** library which can be found at http://www.ossp.org/pkg/lib/var/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** var.c: library implementation */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* required system headers */ #include #include #include #include #include #include #include #if defined(HAVE_PCREPOSIX) # include #else # include #endif #if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) #include #endif /* required internal headers */ #include "var.h" /* unique library identifier */ const char var_id[] = "OSSP var"; /* support for OSSP ex based exception throwing */ #ifdef WITH_EX #include "ex.h" #define VAR_RC(rv) \ ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) ) #else #define VAR_RC(rv) (rv) #endif /* WITH_EX */ #ifndef NUL #define NUL '\0' #endif /* ** ** ==== INTERNAL DATA STRUCTURES ==== ** */ typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */ /* the external context structure */ struct var_st { var_syntax_t syntax; char_class_t syntax_nameclass; var_cb_value_t cb_value_fct; void *cb_value_ctx; var_cb_operation_t cb_operation_fct; void *cb_operation_ctx; }; /* the internal expansion context structure */ struct var_parse_st { struct var_parse_st *lower; int force_expand; int rel_lookup_flag; int rel_lookup_cnt; int index_this; }; typedef struct var_parse_st var_parse_t; /* the default syntax configuration */ static const var_syntax_t var_syntax_default = { '\\', /* escape */ '$', /* delim_init */ '{', /* delim_open */ '}', /* delim_close */ '[', /* index_open */ ']', /* index_close */ '#', /* index_mark */ "a-zA-Z0-9_" /* name_chars */ }; /* ** ** ==== FORMATTING FUNCTIONS ==== ** */ /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */ static int var_mvxprintf( int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, const char *format, va_list ap) { /* sufficient integer buffer: x log_10(2) + safety */ char ibuf[((sizeof(int)*8)/3)+10]; char *cp; char c; int d; int n; int bytes; if (format == NULL) return -1; bytes = 0; while (*format != '\0') { if (*format == '%') { c = *(format+1); if (c == '%') { /* expand "%%" */ cp = &c; n = sizeof(char); } else if (c == 'c') { /* expand "%c" */ c = (char)va_arg(ap, int); cp = &c; n = sizeof(char); } else if (c == 's') { /* expand "%s" */ if ((cp = (char *)va_arg(ap, char *)) == NULL) cp = "(null)"; n = strlen(cp); } else if (c == 'd') { /* expand "%d" */ d = (int)va_arg(ap, int); #ifdef HAVE_SNPRINTF snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ #else sprintf(ibuf, "%d", d); /* implicitly secure */ #endif cp = ibuf; n = strlen(cp); } else { /* any other "%X" */ cp = (char *)format; n = 2; } format += 2; } else { /* plain text */ cp = (char *)format; if ((format = strchr(cp, '%')) == NULL) format = strchr(cp, '\0'); n = format - cp; } /* perform output operation */ if (output != NULL) if ((n = output(ctx, cp, n)) == -1) break; bytes += n; } return bytes; } /* output callback function context for var_mvsnprintf() */ typedef struct { char *bufptr; size_t buflen; } var_mvsnprintf_cb_t; /* output callback function for var_mvsnprintf() */ static int var_mvsnprintf_cb( void *_ctx, const char *buffer, size_t bufsize) { var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx; if (bufsize > ctx->buflen) return -1; memcpy(ctx->bufptr, buffer, bufsize); ctx->bufptr += bufsize; ctx->buflen -= bufsize; return bufsize; } /* minimal vsnprintf(3) variant which supports %{c,s,d} only */ static int var_mvsnprintf( char *buffer, size_t bufsize, const char *format, va_list ap) { int n; var_mvsnprintf_cb_t ctx; if (format == NULL) return -1; if (buffer != NULL && bufsize == 0) return -1; if (buffer == NULL) /* just determine output length */ n = var_mvxprintf(NULL, NULL, format, ap); else { /* perform real output */ ctx.bufptr = buffer; ctx.buflen = bufsize; n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap); if (n != -1 && ctx.buflen == 0) n = -1; if (n != -1) *(ctx.bufptr) = '\0'; } return n; } /* ** ** ==== PARSE CONTEXT FUNCTIONS ==== ** */ static var_parse_t * var_parse_push( var_parse_t *lower, var_parse_t *upper) { if (upper == NULL) return NULL; memcpy(upper, lower, sizeof(var_parse_t)); upper->lower = lower; return upper; } static var_parse_t * var_parse_pop( var_parse_t *upper) { if (upper == NULL) return NULL; return upper->lower; } /* ** ** ==== TOKEN BUFFER FUNCTIONS ==== ** */ #define TOKENBUF_INITIAL_BUFSIZE 64 typedef struct { const char *begin; const char *end; size_t buffer_size; } tokenbuf_t; static void tokenbuf_init( tokenbuf_t *buf) { buf->begin = NULL; buf->end = NULL; buf->buffer_size = 0; return; } static int tokenbuf_isundef( tokenbuf_t *buf) { if (buf->begin == NULL && buf->end == NULL) return 1; return 0; } static int tokenbuf_isempty( tokenbuf_t *buf) { if (buf->begin == buf->end) return 1; return 0; } static void tokenbuf_set( tokenbuf_t *buf, const char *begin, const char *end, size_t buffer_size) { buf->begin = begin; buf->end = end; buf->buffer_size = buffer_size; return; } static void tokenbuf_move( tokenbuf_t *src, tokenbuf_t *dst) { dst->begin = src->begin; dst->end = src->end; dst->buffer_size = src->buffer_size; tokenbuf_init(src); return; } static int tokenbuf_assign( tokenbuf_t *buf, const char *data, size_t len) { char *p; if ((p = (char *)malloc(len + 1)) == NULL) return 0; memcpy(p, data, len); buf->begin = p; buf->end = p + len; buf->buffer_size = len + 1; *((char *)(buf->end)) = NUL; return 1; } static int tokenbuf_append( tokenbuf_t *output, const char *data, size_t len) { char *new_buffer; size_t new_size; char *tmp; /* Is the tokenbuffer initialized at all? If not, allocate a standard-sized buffer to begin with. */ if (output->begin == NULL) { if ((output->begin = output->end = (char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL) return 0; output->buffer_size = TOKENBUF_INITIAL_BUFSIZE; } /* does the token contain text, but no buffer has been allocated yet? */ if (output->buffer_size == 0) { /* check whether data borders to output. If, we can append simly by increasing the end pointer. */ if (output->end == data) { output->end += len; return 1; } /* ok, so copy the contents of output into an allocated buffer so that we can append that way. */ if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL) return 0; memcpy(tmp, output->begin, output->end - output->begin); output->buffer_size = output->end - output->begin; output->begin = tmp; output->end = tmp + output->buffer_size; output->buffer_size += len + 1; } /* does the token fit into the current buffer? If not, realloc a larger buffer that fits. */ if ((output->buffer_size - (output->end - output->begin)) <= len) { new_size = output->buffer_size; do { new_size *= 2; } while ((new_size - (output->end - output->begin)) <= len); if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL) return 0; output->end = new_buffer + (output->end - output->begin); output->begin = new_buffer; output->buffer_size = new_size; } /* append the data at the end of the current buffer. */ if (len > 0) memcpy((char *)output->end, data, len); output->end += len; *((char *)output->end) = NUL; return 1; } static int tokenbuf_merge( tokenbuf_t *output, tokenbuf_t *input) { return tokenbuf_append(output, input->begin, input->end - input->begin); } static void tokenbuf_free( tokenbuf_t *buf) { if (buf->begin != NULL && buf->buffer_size > 0) free((char *)buf->begin); buf->begin = buf->end = NULL; buf->buffer_size = 0; return; } /* ** ** ==== CHARACTER CLASS EXPANSION ==== ** */ static void expand_range( char a, char b, char_class_t cclass) { do { cclass[(int)a] = 1; } while (++a <= b); return; } static var_rc_t expand_character_class( const char *desc, char_class_t cclass) { size_t i; /* clear the class array. */ for (i = 0; i < 256; ++i) cclass[i] = 0; /* walk through class description and set appropriate entries in array */ while (*desc != NUL) { if (desc[1] == '-' && desc[2] != NUL) { if (desc[0] > desc[2]) return VAR_ERR_INCORRECT_CLASS_SPEC; expand_range(desc[0], desc[2], cclass); desc += 3; } else { cclass[(int) *desc] = 1; desc++; } } return VAR_OK; } /* ** ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ==== ** */ static int expand_isoct( char c) { if (c >= '0' && c <= '7') return 1; else return 0; } static var_rc_t expand_octal( const char **src, char **dst, const char *end) { unsigned char c; if (end - *src < 3) return VAR_ERR_INCOMPLETE_OCTAL; if ( !expand_isoct(**src) || !expand_isoct((*src)[1]) || !expand_isoct((*src)[2])) return VAR_ERR_INVALID_OCTAL; c = **src - '0'; if (c > 3) return VAR_ERR_OCTAL_TOO_LARGE; c *= 8; (*src)++; c += **src - '0'; c *= 8; (*src)++; c += **src - '0'; **dst = (char) c; (*dst)++; return VAR_OK; } static int expand_ishex( char c) { if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return 1; else return 0; } static var_rc_t expand_simple_hex( const char **src, char **dst, const char *end) { unsigned char c = 0; if (end - *src < 2) return VAR_ERR_INCOMPLETE_HEX; if ( !expand_ishex(**src) || !expand_ishex((*src)[1])) return VAR_ERR_INVALID_HEX; if (**src >= '0' && **src <= '9') c = **src - '0'; else if (**src >= 'a' && **src <= 'f') c = **src - 'a' + 10; else if (**src >= 'A' && **src <= 'F') c = **src - 'A' + 10; c = c << 4; (*src)++; if (**src >= '0' && **src <= '9') c += **src - '0'; else if (**src >= 'a' && **src <= 'f') c += **src - 'a' + 10; else if (**src >= 'A' && **src <= 'F') c += **src - 'A' + 10; **dst = (char)c; (*dst)++; return VAR_OK; } static var_rc_t expand_grouped_hex( const char **src, char **dst, const char *end) { var_rc_t rc; while (*src < end && **src != '}') { if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK) return rc; (*src)++; } if (*src == end) return VAR_ERR_INCOMPLETE_GROUPED_HEX; return VAR_OK; } static var_rc_t expand_hex( const char **src, char **dst, const char *end) { if (*src == end) return VAR_ERR_INCOMPLETE_HEX; if (**src == '{') { (*src)++; return expand_grouped_hex(src, dst, end); } else return expand_simple_hex(src, dst, end); } /* ** ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ==== ** */ /* forward declarations */ static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result); static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed); static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end); /* parse pattern text */ static int parse_pattern( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until '/' */ for (p = begin; p != end && *p != '/'; p++) { if (*p == var->syntax.escape) { if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; p++; } } return (p - begin); } /* parse substitution text */ static int parse_substext( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until delim_init or '/' */ for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) { if (*p == var->syntax.escape) { if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; p++; } } return (p - begin); } /* parse expression text */ static int parse_exptext( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until delim_init or delim_close or ':' */ for (p = begin; p != end && *p != var->syntax.delim_init && *p != var->syntax.delim_close && *p != ':'; p++) { if (*p == var->syntax.escape) { if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; p++; } } return (p - begin); } /* parse opertion argument text */ static int parse_opargtext( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until delim_init or ')' */ for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) { if (*p == var->syntax.escape) { if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; p++; } } return (p - begin); } static int parse_opargtext_or_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p; tokenbuf_t tmp; int rc; tokenbuf_init(result); tokenbuf_init(&tmp); p = begin; if (p == end) return 0; do { rc = parse_opargtext(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(result, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { p += rc; if (!tokenbuf_merge(result, &tmp)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_free(&tmp); } } while (rc > 0); tokenbuf_free(&tmp); return (p - begin); error_return: tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } /* parse expression or variable */ static int parse_exptext_or_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p = begin; tokenbuf_t tmp; int rc; tokenbuf_init(result); tokenbuf_init(&tmp); if (begin == end) return 0; do { /* try to parse expression text */ rc = parse_exptext(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(result, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } /* try to parse variable construct */ rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { p += rc; if (!tokenbuf_merge(result, &tmp)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_free(&tmp); } } while (rc > 0); tokenbuf_free(&tmp); return (p - begin); error_return: tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } /* parse substitution text or variable */ static int parse_substext_or_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p = begin; tokenbuf_t tmp; int rc; tokenbuf_init(result); tokenbuf_init(&tmp); if (begin == end) return 0; do { /* try to parse substitution text */ rc = parse_substext(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(result, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } /* try to parse substitution text */ rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { p += rc; if (!tokenbuf_merge(result, &tmp)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_free(&tmp); } } while (rc > 0); tokenbuf_free(&tmp); return (p - begin); error_return: tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } /* parse class description */ static int parse_class_description( var_t *var, var_parse_t *ctx, tokenbuf_t *src, tokenbuf_t *dst) { unsigned char c, d; const char *p; p = src->begin; while (p != src->end) { if ((src->end - p) >= 3 && p[1] == '-') { if (*p > p[2]) return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC; for (c = *p, d = p[2]; c <= d; ++c) { if (!tokenbuf_append(dst, (char *)&c, 1)) return VAR_ERR_OUT_OF_MEMORY; } p += 3; } else { if (!tokenbuf_append(dst, p, 1)) return VAR_ERR_OUT_OF_MEMORY; p++; } } return VAR_OK; } /* parse regex replace part */ static int parse_regex_replace( var_t *var, var_parse_t *ctx, const char *data, tokenbuf_t *orig, regmatch_t *pmatch, tokenbuf_t *expanded) { const char *p; size_t i; p = orig->begin; tokenbuf_init(expanded); while (p != orig->end) { if (*p == '\\') { if (orig->end - p <= 1) { tokenbuf_free(expanded); return VAR_ERR_INCOMPLETE_QUOTED_PAIR; } p++; if (*p == '\\') { if (!tokenbuf_append(expanded, p, 1)) { tokenbuf_free(expanded); return VAR_ERR_OUT_OF_MEMORY; } p++; continue; } if (!isdigit((int)*p)) { tokenbuf_free(expanded); return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE; } i = (*p - '0'); p++; if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) { tokenbuf_free(expanded); return VAR_ERR_SUBMATCH_OUT_OF_RANGE; } if (!tokenbuf_append(expanded, data + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so)) { tokenbuf_free(expanded); return VAR_ERR_OUT_OF_MEMORY; } } else { if (!tokenbuf_append(expanded, p, 1)) { tokenbuf_free(expanded); return VAR_ERR_OUT_OF_MEMORY; } p++; } } return VAR_OK; } /* operation: transpose */ static int op_transpose( var_t *var, var_parse_t *ctx, tokenbuf_t *data, tokenbuf_t *search, tokenbuf_t *replace) { tokenbuf_t srcclass, dstclass; const char *p; int rc; int i; tokenbuf_init(&srcclass); tokenbuf_init(&dstclass); if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK) goto error_return; if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK) goto error_return; if (srcclass.begin == srcclass.end) { rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS; goto error_return; } if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) { rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH; goto error_return; } if (data->buffer_size == 0) { tokenbuf_t tmp; if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_move(&tmp, data); } for (p = data->begin; p != data->end; ++p) { for (i = 0; i <= (int)(srcclass.end - srcclass.begin); i++) { if (*p == srcclass.begin[i]) { *((char *)p) = dstclass.begin[i]; break; } } } tokenbuf_free(&srcclass); tokenbuf_free(&dstclass); return VAR_OK; error_return: tokenbuf_free(search); tokenbuf_free(replace); tokenbuf_free(&srcclass); tokenbuf_free(&dstclass); return rc; } /* operation: search & replace */ static int op_search_and_replace( var_t *var, var_parse_t *ctx, tokenbuf_t *data, tokenbuf_t *search, tokenbuf_t *replace, tokenbuf_t *flags) { tokenbuf_t tmp; const char *p; int case_insensitive = 0; int multiline = 0; int global = 0; int no_regex = 0; int rc; if (search->begin == search->end) return VAR_ERR_EMPTY_SEARCH_STRING; for (p = flags->begin; p != flags->end; p++) { switch (tolower(*p)) { case 'm': multiline = 1; break; case 'i': case_insensitive = 1; break; case 'g': global = 1; break; case 't': no_regex = 1; break; default: return VAR_ERR_UNKNOWN_REPLACE_FLAG; } } if (no_regex) { /* plain text pattern based operation */ tokenbuf_init(&tmp); for (p = data->begin; p != data->end; ) { if (case_insensitive) rc = strncasecmp(p, search->begin, search->end - search->begin); else rc = strncmp(p, search->begin, search->end - search->begin); if (rc != 0) { /* not matched, copy character */ if (!tokenbuf_append(&tmp, p, 1)) { tokenbuf_free(&tmp); return VAR_ERR_OUT_OF_MEMORY; } p++; } else { /* matched, copy replacement string */ tokenbuf_merge(&tmp, replace); p += (search->end - search->begin); if (!global) { /* append remaining text */ if (!tokenbuf_append(&tmp, p, data->end - p)) { tokenbuf_free(&tmp); return VAR_ERR_OUT_OF_MEMORY; } break; } } } tokenbuf_free(data); tokenbuf_move(&tmp, data); } else { /* regular expression pattern based operation */ tokenbuf_t mydata; tokenbuf_t myreplace; regex_t preg; regmatch_t pmatch[10]; int regexec_flag; /* copy pattern and data to own buffer to make sure they are NUL-terminated */ if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin)) return VAR_ERR_OUT_OF_MEMORY; if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) { tokenbuf_free(&tmp); return VAR_ERR_OUT_OF_MEMORY; } /* compile the pattern. */ rc = regcomp(&preg, tmp.begin, ( REG_EXTENDED | (multiline ? REG_NEWLINE : 0) | (case_insensitive ? REG_ICASE : 0))); tokenbuf_free(&tmp); if (rc != 0) { tokenbuf_free(&mydata); return VAR_ERR_INVALID_REGEX_IN_REPLACE; } /* match the pattern and create the result string in the tmp buffer */ tokenbuf_append(&tmp, "", 0); for (p = mydata.begin; p < mydata.end; ) { if (p == mydata.begin || p[-1] == '\n') regexec_flag = 0; else regexec_flag = REG_NOTBOL; rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag); if (rc != 0) { /* no (more) matching */ tokenbuf_append(&tmp, p, mydata.end - p); break; } else if ( multiline && (p + pmatch[0].rm_so) == mydata.end && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) { /* special case: found empty pattern (usually /^/ or /$/ only) in multi-line at end of data (after the last newline) */ tokenbuf_append(&tmp, p, mydata.end - p); break; } else { /* append prolog string */ if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); return VAR_ERR_OUT_OF_MEMORY; } /* create replace string */ rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace); if (rc != VAR_OK) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); return rc; } /* append replace string */ if (!tokenbuf_merge(&tmp, &myreplace)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); tokenbuf_free(&myreplace); return VAR_ERR_OUT_OF_MEMORY; } tokenbuf_free(&myreplace); /* skip now processed data */ p += pmatch[0].rm_eo; /* if pattern matched an empty part (think about anchor-only regular expressions like /^/ or /$/) we skip the next character to make sure we do not enter an infinitive loop in matching */ if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) { if (p >= mydata.end) break; if (!tokenbuf_append(&tmp, p, 1)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); return VAR_ERR_OUT_OF_MEMORY; } p++; } /* append prolog string and stop processing if we do not perform the search & replace globally */ if (!global) { if (!tokenbuf_append(&tmp, p, mydata.end - p)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); return VAR_ERR_OUT_OF_MEMORY; } break; } } } regfree(&preg); tokenbuf_free(data); tokenbuf_move(&tmp, data); tokenbuf_free(&mydata); } return VAR_OK; } /* operation: offset substring */ static int op_offset( var_t *var, var_parse_t *ctx, tokenbuf_t *data, int num1, int num2, int isrange) { tokenbuf_t res; const char *p; /* determine begin of result string */ if ((data->end - data->begin) < num1) return VAR_ERR_OFFSET_OUT_OF_BOUNDS; p = data->begin + num1; /* if num2 is zero, we copy the rest from there. */ if (num2 == 0) { if (!tokenbuf_assign(&res, p, data->end - p)) return VAR_ERR_OUT_OF_MEMORY; } else { /* ok, then use num2. */ if (isrange) { if ((p + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS; if (!tokenbuf_assign(&res, p, num2)) return VAR_ERR_OUT_OF_MEMORY; } else { if (num2 < num1) return VAR_ERR_OFFSET_LOGIC; if ((data->begin + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS; if (!tokenbuf_assign(&res, p, num2 - num1 + 1)) return VAR_ERR_OUT_OF_MEMORY; } } tokenbuf_free(data); tokenbuf_move(&res, data); return VAR_OK; } /* operation: padding */ static int op_padding( var_t *var, var_parse_t *ctx, tokenbuf_t *data, int width, tokenbuf_t *fill, char position) { tokenbuf_t result; int i; if (fill->begin == fill->end) return VAR_ERR_EMPTY_PADDING_FILL_STRING; tokenbuf_init(&result); if (position == 'l') { /* left padding */ i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while (i > 0) { if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin)) return VAR_ERR_OUT_OF_MEMORY; i--; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!tokenbuf_append(data, fill->begin, i)) return VAR_ERR_OUT_OF_MEMORY; } } else if (position == 'r') { /* right padding */ i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while (i > 0) { if (!tokenbuf_merge(&result, fill)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } i--; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!tokenbuf_append(&result, fill->begin, i)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } if (!tokenbuf_merge(&result, data)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } /* move string from temporary buffer to data buffer */ tokenbuf_free(data); tokenbuf_move(&result, data); } } else if (position == 'c') { /* centered padding */ i = (width - (data->end - data->begin)) / 2; if (i > 0) { /* create the prefix */ i = i / (fill->end - fill->begin); while (i > 0) { if (!tokenbuf_merge(&result, fill)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } i--; } i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin); if (!tokenbuf_append(&result, fill->begin, i)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } /* append the actual data string */ if (!tokenbuf_merge(&result, data)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } /* append the suffix */ i = width - (result.end - result.begin); i = i / (fill->end - fill->begin); while (i > 0) { if (!tokenbuf_merge(&result, fill)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } i--; } i = width - (result.end - result.begin); if (!tokenbuf_append(&result, fill->begin, i)) { tokenbuf_free(&result); return VAR_ERR_OUT_OF_MEMORY; } /* move string from temporary buffer to data buffer */ tokenbuf_free(data); tokenbuf_move(&result, data); } } return VAR_OK; } /* parse an integer number ("123") */ static int parse_integer( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result) { const char *p; int num; p = begin; num = 0; while (isdigit(*p) && p != end) { num *= 10; num += (*p - '0'); p++; } if (result != NULL) *result = num; return (p - begin); } /* parse an operation (":x...") */ static int parse_operation( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *data) { const char *p; tokenbuf_t tmptokbuf; tokenbuf_t search, replace, flags; tokenbuf_t number1, number2; int num1, num2; int isrange; int rc; char *ptr; /* initialization */ tokenbuf_init(&tmptokbuf); tokenbuf_init(&search); tokenbuf_init(&replace); tokenbuf_init(&flags); tokenbuf_init(&number1); tokenbuf_init(&number2); p = begin; if (p == end) return 0; /* dispatch through the first operation character */ switch (tolower(*p)) { case 'l': { /* turn value to lowercase. */ if (data->begin != NULL) { /* if the buffer does not live in an allocated buffer, we have to copy it before modifying the contents. */ if (data->buffer_size == 0) { if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } } /* convert value */ for (ptr = (char *)data->begin; ptr != data->end; ptr++) *ptr = (char)tolower((int)(*ptr)); } p++; break; } case 'u': { /* turn value to uppercase. */ if (data->begin != NULL) { /* if the buffer does not live in an allocated buffer, we have to copy it before modifying the contents. */ if (data->buffer_size == 0) { if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } } /* convert value */ for (ptr = (char *)data->begin; ptr != data->end; ptr++) *ptr = (char)toupper((int)(*ptr)); } p++; break; } case 'o': { /* cut out substring of value. */ p++; rc = parse_integer(var, ctx, p, end, &num1); if (rc == 0) { rc = VAR_ERR_MISSING_START_OFFSET; goto error_return; } else if (rc < 0) goto error_return; p += rc; if (*p == ',') { isrange = 0; p++; } else if (*p == '-') { isrange = 1; p++; } else { rc = VAR_ERR_INVALID_OFFSET_DELIMITER; goto error_return; } rc = parse_integer(var, ctx, p, end, &num2); p += rc; if (data->begin != NULL) { rc = op_offset(var, ctx, data, num1, num2, isrange); if (rc < 0) goto error_return; } break; } case '#': { /* determine length of the value */ if (data->begin != NULL) { char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */ sprintf(buf, "%d", (int)(data->end - data->begin)); tokenbuf_free(data); if (!tokenbuf_assign(data, buf, strlen(buf))) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } } p++; break; } case '-': { /* substitute parameter if data is empty */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (tokenbuf_isundef(data)) tokenbuf_move(&tmptokbuf, data); else if (tokenbuf_isempty(data)) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } break; } case '*': { /* substitute empty string if data is not empty, parameter otherwise. */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (data->begin != NULL) { if (data->begin == data->end) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } else { tokenbuf_free(data); data->begin = data->end = ""; data->buffer_size = 0; } } break; } case '+': { /* substitute parameter if data is not empty. */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (data->begin != NULL && data->begin != data->end) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } break; } case 's': { /* search and replace. */ p++; if (*p != '/') return VAR_ERR_MALFORMATTED_REPLACE; p++; rc = parse_pattern(var, ctx, p, end); if (rc < 0) goto error_return; tokenbuf_set(&search, p, p + rc, 0); p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_REPLACE; goto error_return; } p++; rc = parse_substext_or_variable(var, ctx, p, end, &replace); if (rc < 0) goto error_return; p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_REPLACE; goto error_return; } p++; rc = parse_exptext(var, ctx, p, end); if (rc < 0) goto error_return; tokenbuf_set(&flags, p, p + rc, 0); p += rc; if (data->begin != NULL) { rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags); if (rc < 0) goto error_return; } break; } case 'y': { /* transpose characters from class A to class B. */ p++; if (*p != '/') return VAR_ERR_MALFORMATTED_TRANSPOSE; p++; rc = parse_substext_or_variable(var, ctx, p, end, &search); if (rc < 0) goto error_return; p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_TRANSPOSE; goto error_return; } p++; rc = parse_substext_or_variable(var, ctx, p, end, &replace); if (rc < 0) goto error_return; p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_TRANSPOSE; goto error_return; } else p++; if (data->begin) { rc = op_transpose(var, ctx, data, &search, &replace); if (rc < 0) goto error_return; } break; } case 'p': { /* padding. */ p++; if (*p != '/') return VAR_ERR_MALFORMATTED_PADDING; p++; rc = parse_integer(var, ctx, p, end, &num1); if (rc == 0) { rc = VAR_ERR_MISSING_PADDING_WIDTH; goto error_return; } p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; rc = parse_substext_or_variable(var, ctx, p, end, &replace); if (rc < 0) goto error_return; p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; if (*p != 'l' && *p != 'c' && *p != 'r') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; if (data->begin) { rc = op_padding(var, ctx, data, num1, &replace, p[-1]); if (rc < 0) goto error_return; } break; } case '%': { /* operation callback function */ const char *op_ptr; size_t op_len; const char *arg_ptr; size_t arg_len; const char *val_ptr; size_t val_len; const char *out_ptr; size_t out_len; size_t out_size; tokenbuf_t args; p++; rc = parse_name(var, ctx, p, end); if (rc < 0) goto error_return; op_ptr = p; op_len = rc; p += rc; if (*p == '(') { p++; tokenbuf_init(&args); rc = parse_opargtext_or_variable(var, ctx, p, end, &args); if (rc < 0) goto error_return; p += rc; arg_ptr = args.begin; arg_len = args.end - args.begin; if (*p != ')') { rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS; goto error_return; } p++; } else { arg_ptr = NULL; arg_len = 0; } val_ptr = data->begin; val_len = data->end - data->begin; if (data->begin != NULL && var->cb_operation_fct != NULL) { /* call operation callback function */ rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx, op_ptr, op_len, arg_ptr, arg_len, val_ptr, val_len, &out_ptr, &out_len, &out_size); if (rc < 0) { if (arg_ptr != NULL) free((void *)arg_ptr); goto error_return; } tokenbuf_free(data); tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size); } if (arg_ptr != NULL) free((void *)arg_ptr); break; } default: return VAR_ERR_UNKNOWN_COMMAND_CHAR; } /* return successfully */ tokenbuf_free(&tmptokbuf); tokenbuf_free(&search); tokenbuf_free(&replace); tokenbuf_free(&flags); tokenbuf_free(&number1); tokenbuf_free(&number2); return (p - begin); /* return with an error */ error_return: tokenbuf_free(data); tokenbuf_free(&tmptokbuf); tokenbuf_free(&search); tokenbuf_free(&replace); tokenbuf_free(&flags); tokenbuf_free(&number1); tokenbuf_free(&number2); return rc; } /* parse numerical expression operand */ static int parse_numexp_operand( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed) { const char *p; tokenbuf_t tmp; int rc; var_parse_t myctx; /* initialization */ p = begin; tokenbuf_init(&tmp); if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC; /* parse opening numerical expression */ if (*p == '(') { /* parse inner numerical expression */ rc = parse_numexp(var, ctx, ++p, end, result, failed); if (rc < 0) return rc; p += rc; if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC; /* parse closing parenthesis */ if (*p != ')') return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX; p++; } /* parse contained variable */ else if (*p == var->syntax.delim_init) { /* parse variable with forced expansion */ ctx = var_parse_push(ctx, &myctx); ctx->force_expand = 1; rc = parse_variable(var, ctx, p, end, &tmp); ctx = var_parse_pop(ctx); if (rc == VAR_ERR_UNDEFINED_VARIABLE) { *failed = 1; /* parse variable without forced expansion */ ctx = var_parse_push(ctx, &myctx); ctx->force_expand = 0; rc = parse_variable(var, ctx, p, end, &tmp); ctx = var_parse_pop(ctx); if (rc < 0) return rc; p += rc; *result = 0; } else if (rc < 0) return rc; else { p += rc; /* parse remaining numerical expression */ rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed); tokenbuf_free(&tmp); if (rc < 0) return rc; } } /* parse relative index mark ("#") */ else if ( var->syntax.index_mark != NUL && *p == var->syntax.index_mark) { p++; *result = ctx->index_this; if (ctx->rel_lookup_flag) ctx->rel_lookup_cnt++; } /* parse plain integer number */ else if (isdigit(*p)) { rc = parse_integer(var, ctx, p, end, result); p += rc; } /* parse signed positive integer number */ else if (*p == '+') { if ((end - p) > 1 && isdigit(p[1])) { p++; rc = parse_integer(var, ctx, p, end, result); p += rc; } else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; } /* parse signed negative integer number */ else if (*p == '-') { if (end - p > 1 && isdigit(p[1])) { p++; rc = parse_integer(var, ctx, p, end, result); *result = -(*result); p += rc; } else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; } /* else we failed to parse anything reasonable */ else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; return (p - begin); } /* parse numerical expression ("x+y") */ static int parse_numexp( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed) { const char *p; char op; int right; int rc; /* initialization */ p = begin; if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC; /* parse left numerical operand */ rc = parse_numexp_operand(var, ctx, p, end, result, failed); if (rc < 0) return rc; p += rc; /* parse numerical operator */ while (p != end) { if (*p == '+' || *p == '-') { op = *p++; /* recursively parse right operand (light binding) */ rc = parse_numexp(var, ctx, p, end, &right, failed); if (rc < 0) return rc; p += rc; if (op == '+') *result = (*result + right); else *result = (*result - right); } else if (*p == '*' || *p == '/' || *p == '%') { op = *p++; /* recursively parse right operand (string binding) */ rc = parse_numexp_operand(var, ctx, p, end, &right, failed); if (rc < 0) return rc; p += rc; if (op == '*') *result = (*result * right); else if (op == '/') { if (right == 0) { if (*failed) *result = 0; else return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; } else *result = (*result / right); } else if (op == '%') { if (right == 0) { if (*failed) *result = 0; else return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; } else *result = (*result % right); } } else break; } /* return amount of parsed input */ return (p - begin); } /* parse variable name ("abc") */ static int parse_name( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse as long as name class characters are found */ for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++) ; return (p - begin); } /* lookup a variable value through the callback function */ static int lookup_value( var_t *var, var_parse_t *ctx, const char *var_ptr, size_t var_len, int var_idx, const char **val_ptr, size_t *val_len, size_t *val_size) { char buf[1]; int rc; /* pass through to original callback */ rc = (*var->cb_value_fct)(var, var->cb_value_ctx, var_ptr, var_len, var_idx, val_ptr, val_len, val_size); /* convert undefined variable into empty variable if relative lookups are counted. This is the case inside an active loop construct if no limits are given. There the parse_input() has to proceed until all variables have undefined values. This trick here allows it to determine this case. */ if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) { ctx->rel_lookup_cnt--; buf[0] = NUL; *val_ptr = buf; *val_len = 0; *val_size = 0; return VAR_OK; } return rc; } /* parse complex variable construct ("${name...}") */ static int parse_variable_complex( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p; const char *data; size_t len, buffer_size; int failed = 0; int rc; int idx = 0; tokenbuf_t name; tokenbuf_t tmp; /* initializations */ p = begin; tokenbuf_init(&name); tokenbuf_init(&tmp); tokenbuf_init(result); /* parse open delimiter */ if (p == end || *p != var->syntax.delim_open) return 0; p++; if (p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; /* parse name of variable to expand. The name may consist of an arbitrary number of variable name character and contained variable constructs. */ do { /* parse a variable name */ rc = parse_name(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(&name, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } /* parse an (embedded) variable */ rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_merge(&name, &tmp)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_free(&tmp); p += rc; } } while (rc > 0); /* we must have the complete expanded variable name now, so make sure we really do. */ if (name.begin == name.end) { if (ctx->force_expand) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } else { /* If no force_expand is requested, we have to back-off. We're not sure whether our approach here is 100% correct, because it _could_ have side-effects according to Peter Simons, but as far as we know and tried it, it is correct. But be warned -- RSE */ tokenbuf_set(result, begin - 1, p, 0); goto goahead; } } /* parse an optional index specification */ if ( var->syntax.index_open != NUL && *p == var->syntax.index_open) { p++; rc = parse_numexp(var, ctx, p, end, &idx, &failed); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; goto error_return; } p += rc; if (p == end) { rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; goto error_return; } if (*p != var->syntax.index_close) { rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; goto error_return; } p++; } /* parse end of variable construct or start of post-operations */ if (p == end || (*p != var->syntax.delim_close && *p != ':')) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } p++; /* lookup the variable value now */ if (failed) tokenbuf_set(result, begin - 1, p, 0); else { rc = lookup_value(var, ctx, name.begin, name.end-name.begin, idx, &data, &len, &buffer_size); if (rc == VAR_ERR_UNDEFINED_VARIABLE) tokenbuf_init(result); /* delayed handling of undefined variable */ else if (rc < 0) goto error_return; else /* the preliminary result is the raw value of the variable. This may be modified by the operations that may follow. */ tokenbuf_set(result, data, data + len, buffer_size); } /* parse optional post-operations */ goahead: if (p[-1] == ':') { tokenbuf_free(&tmp); tokenbuf_init(&tmp); p--; while (p != end && *p == ':') { p++; if (!failed) rc = parse_operation(var, ctx, p, end, result); else rc = parse_operation(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; p += rc; if (failed) result->end += rc; } if (p == end || *p != var->syntax.delim_close) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } p++; if (failed) result->end++; } /* lazy handling of undefined variable */ if (!failed && tokenbuf_isundef(result)) { if (ctx->force_expand) { rc = VAR_ERR_UNDEFINED_VARIABLE; goto error_return; } else tokenbuf_set(result, begin - 1, p, 0); } /* return successfully */ tokenbuf_free(&name); tokenbuf_free(&tmp); return (p - begin); /* return with an error */ error_return: tokenbuf_free(&name); tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } /* parse variable construct ("$name" or "${name...}") */ static int parse_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p; const char *data; size_t len, buffer_size; int rc, rc2; /* initialization */ p = begin; tokenbuf_init(result); /* parse init delimiter */ if (p == end || *p != var->syntax.delim_init) return 0; p++; if (p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; /* parse a simple variable name. (if this fails, we're try to parse a complex variable construct) */ rc = parse_name(var, ctx, p, end); if (rc < 0) return rc; if (rc > 0) { rc2 = lookup_value(var, ctx, p, rc, 0, &data, &len, &buffer_size); if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) { tokenbuf_set(result, begin, begin + 1 + rc, 0); return (1 + rc); } if (rc2 < 0) return rc2; tokenbuf_set(result, data, data + len, buffer_size); return (1 + rc); } /* parse a complex variable construct (else case) */ rc = parse_variable_complex(var, ctx, p, end, result); if (rc > 0) rc++; return rc; } /* parse loop construct limits ("[...]{b,s,e}") */ static int parse_looplimits( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *start, int *step, int *stop, int *open_stop) { const char *p; int rc; int failed; /* initialization */ p = begin; /* we are happy if nothing is to left to parse */ if (p == end) return 0; /* parse start delimiter */ if (*p != var->syntax.delim_open) return 0; p++; /* parse loop start value */ failed = 0; rc = parse_numexp(var, ctx, p, end, start, &failed); if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) *start = 0; /* use default */ else if (rc < 0) return rc; else p += rc; if (failed) return VAR_ERR_UNDEFINED_VARIABLE; /* parse separator */ if (*p != ',') return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; p++; /* parse loop step value */ failed = 0; rc = parse_numexp(var, ctx, p, end, step, &failed); if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) *step = 1; /* use default */ else if (rc < 0) return rc; else p += rc; if (failed) return VAR_ERR_UNDEFINED_VARIABLE; /* parse separator */ if (*p != ',') { /* if not found, parse end delimiter */ if (*p != var->syntax.delim_close) return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; p++; /* shift step value to stop value */ *stop = *step; *step = 1; /* determine whether loop end is open */ if (rc > 0) *open_stop = 0; else *open_stop = 1; return (p - begin); } p++; /* parse loop stop value */ failed = 0; rc = parse_numexp(var, ctx, p, end, stop, &failed); if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) { *stop = 0; /* use default */ *open_stop = 1; } else if (rc < 0) return rc; else { *open_stop = 0; p += rc; } if (failed) return VAR_ERR_UNDEFINED_VARIABLE; /* parse end delimiter */ if (*p != var->syntax.delim_close) return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; p++; /* return amount of parsed input */ return (p - begin); } /* parse plain text */ static int parse_text( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until delim_init (variable construct) or index_open (loop construct) is found */ for (p = begin; p != end; p++) { if (*p == var->syntax.escape) { p++; /* skip next character */ if (p == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; } else if (*p == var->syntax.delim_init) break; else if ( var->syntax.index_open != NUL && ( *p == var->syntax.index_open || *p == var->syntax.index_close)) break; } return (p - begin); } /* expand input in general */ static int parse_input( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *output, int recursion_level) { const char *p; int rc, rc2; tokenbuf_t result; int start, step, stop, open_stop; int i; int output_backup; int rel_lookup_cnt; int loop_limit_length; var_parse_t myctx; /* initialization */ p = begin; do { /* try to parse a loop construct */ if ( p != end && var->syntax.index_open != NUL && *p == var->syntax.index_open) { p++; /* loop preparation */ loop_limit_length = -1; rel_lookup_cnt = ctx->rel_lookup_cnt; open_stop = 1; rc = 0; start = 0; step = 1; stop = 0; output_backup = 0; /* iterate over loop construct, either as long as there is (still) nothing known about the limit, or there is an open (=unknown) limit stop and there are still defined variables or there is a stop limit known and it is still not reached */ re_loop: for (i = start; ( ( open_stop && ( loop_limit_length < 0 || rel_lookup_cnt > ctx->rel_lookup_cnt)) || ( !open_stop && i <= stop) ); i += step) { /* remember current output end for restoring */ output_backup = (output->end - output->begin); /* open temporary context for recursion */ ctx = var_parse_push(ctx, &myctx); ctx->force_expand = 1; ctx->rel_lookup_flag = 1; ctx->index_this = i; /* recursive parse input through ourself */ rc = parse_input(var, ctx, p, end, output, recursion_level+1); /* retrieve info and close temporary context */ rel_lookup_cnt = ctx->rel_lookup_cnt; ctx = var_parse_pop(ctx); /* error handling */ if (rc < 0) goto error_return; /* make sure the loop construct is closed */ if (p[rc] != var->syntax.index_close) { rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT; goto error_return; } /* try to parse loop construct limit specification */ if (loop_limit_length < 0) { rc2 = parse_looplimits(var, ctx, p+rc+1, end, &start, &step, &stop, &open_stop); if (rc2 < 0) goto error_return; else if (rc2 == 0) loop_limit_length = 0; else if (rc2 > 0) { loop_limit_length = rc2; /* restart loop from scratch */ output->end = (output->begin + output_backup); goto re_loop; } } } /* if stop value is open, restore to the output end because the last iteration was just to determine the loop termination and its result has to be discarded */ if (open_stop) output->end = (output->begin + output_backup); /* skip parsed loop construct */ p += rc; p++; p += loop_limit_length; continue; } /* try to parse plain text */ rc = parse_text(var, ctx, p, end); if (rc > 0) { if (!tokenbuf_append(output, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; continue; } else if (rc < 0) goto error_return; /* try to parse a variable construct */ tokenbuf_init(&result); rc = parse_variable(var, ctx, p, end, &result); if (rc > 0) { if (!tokenbuf_merge(output, &result)) { tokenbuf_free(&result); rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_free(&result); p += rc; continue; } tokenbuf_free(&result); if (rc < 0) goto error_return; } while (p != end && rc > 0); /* We do not know whether this really could happen, but because we are paranoid, report an error at the outer most parsing level if there is still any input. Because this would mean that we are no longer able to parse the remaining input as a loop construct, a text or a variable construct. This would be very strange, but could perhaps happen in case of configuration errors!?... */ if (recursion_level == 0 && p != end) { rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE; goto error_return; } /* return amount of parsed text */ return (p - begin); /* return with an error where as a special case the output begin is set to the input begin and the output end to the last input parsing position. */ error_return: tokenbuf_free(output); tokenbuf_set(output, begin, p, 0); return rc; } /* ** ** ==== APPLICATION PROGRAMMING INTERFACE (API) ==== ** */ /* create variable expansion context */ var_rc_t var_create( var_t **pvar) { var_t *var; if (pvar == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); if ((var = (var_t *)malloc(sizeof(var_t))) == NULL) return VAR_RC(VAR_ERR_OUT_OF_MEMORY); memset(var, 0, sizeof(var)); var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default); *pvar = var; return VAR_OK; } /* destroy variable expansion context */ var_rc_t var_destroy( var_t *var) { if (var == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); free(var); return VAR_OK; } /* configure variable expansion context */ var_rc_t var_config( var_t *var, var_config_t mode, ...) { va_list ap; var_rc_t rc; if (var == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); va_start(ap, mode); switch (mode) { case VAR_CONFIG_SYNTAX: { var_syntax_t *s; s = (var_syntax_t *)va_arg(ap, void *); if (s == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); var->syntax.escape = s->escape; var->syntax.delim_init = s->delim_init; var->syntax.delim_open = s->delim_open; var->syntax.delim_close = s->delim_close; var->syntax.index_open = s->index_open; var->syntax.index_close = s->index_close; var->syntax.index_mark = s->index_mark; var->syntax.name_chars = NULL; /* unused internally */ if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK) return VAR_RC(rc); if ( var->syntax_nameclass[(int)var->syntax.delim_init] || var->syntax_nameclass[(int)var->syntax.delim_open] || var->syntax_nameclass[(int)var->syntax.delim_close] || var->syntax_nameclass[(int)var->syntax.escape]) return VAR_RC(VAR_ERR_INVALID_CONFIGURATION); break; } case VAR_CONFIG_CB_VALUE: { var_cb_value_t fct; void *ctx; fct = (var_cb_value_t)va_arg(ap, var_cb_value_t); ctx = (void *)va_arg(ap, void *); var->cb_value_fct = fct; var->cb_value_ctx = ctx; break; } case VAR_CONFIG_CB_OPERATION: { var_cb_operation_t fct; void *ctx; fct = (var_cb_operation_t)va_arg(ap, var_cb_operation_t); ctx = (void *)va_arg(ap, void *); var->cb_operation_fct = fct; var->cb_operation_ctx = ctx; break; } default: return VAR_RC(VAR_ERR_INVALID_ARGUMENT); } va_end(ap); return VAR_OK; } /* perform unescape operation on a buffer */ var_rc_t var_unescape( var_t *var, const char *src, size_t srclen, char *dst, size_t dstlen, int all) { const char *end; var_rc_t rc; if (var == NULL || src == NULL || dst == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); end = src + srclen; while (src < end) { if (*src == '\\') { if (++src == end) return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER); switch (*src) { case '\\': if (!all) { *dst++ = '\\'; } *dst++ = '\\'; break; case 'n': *dst++ = '\n'; break; case 't': *dst++ = '\t'; break; case 'r': *dst++ = '\r'; break; case 'x': ++src; if ((rc = expand_hex(&src, &dst, end)) != VAR_OK) return VAR_RC(rc); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if ( end - src >= 3 && isdigit((int)src[1]) && isdigit((int)src[2])) { if ((rc = expand_octal(&src, &dst, end)) != 0) return VAR_RC(rc); break; } /* FALLTHROUGH */ default: if (!all) { *dst++ = '\\'; } *dst++ = *src; } ++src; } else *dst++ = *src++; } *dst = NUL; return VAR_OK; } /* perform expand operation on a buffer */ var_rc_t var_expand( var_t *var, const char *src_ptr, size_t src_len, char **dst_ptr, size_t *dst_len, int force_expand) { var_parse_t ctx; tokenbuf_t output; var_rc_t rc; /* argument sanity checks */ if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); /* prepare internal expansion context */ ctx.lower = NULL; ctx.force_expand = force_expand; ctx.rel_lookup_flag = 0; ctx.rel_lookup_cnt = 0; ctx.index_this = 0; /* start the parsing */ tokenbuf_init(&output); rc = (var_rc_t)parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0); /* post-processing */ if (rc >= 0) { /* always NUL-terminate output for convenience reasons but do not count the NUL-terminator in the length */ if (!tokenbuf_append(&output, "\0", 1)) { tokenbuf_free(&output); return VAR_RC(VAR_ERR_OUT_OF_MEMORY); } output.end--; /* provide result */ *dst_ptr = (char *)output.begin; if (dst_len != NULL) *dst_len = (output.end - output.begin); rc = VAR_OK; } else { /* provide result */ *dst_ptr = (char *)src_ptr; if (dst_len != NULL) *dst_len = (output.end - output.begin); } return VAR_RC(rc); } /* format and expand a string */ var_rc_t var_formatv( var_t *var, char **dst_ptr, int force_expand, const char *fmt, va_list ap) { var_rc_t rc; va_list apbak; char *cpBuf; int nBuf; /* argument sanity checks */ if (var == NULL || dst_ptr == NULL || fmt == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); /* determine formatting buffer length */ va_copy(apbak, ap); nBuf = var_mvsnprintf(NULL, 0, fmt, ap); va_copy(ap, apbak); if (nBuf == -1) return VAR_RC(VAR_ERR_FORMATTING_FAILURE); /* perform formatting */ if ((cpBuf = (char *)malloc(nBuf+1)) == NULL) return VAR_RC(VAR_ERR_OUT_OF_MEMORY); nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap); if (nBuf == -1) { free(cpBuf); return VAR_RC(VAR_ERR_FORMATTING_FAILURE); } /* perform expansion */ if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) { free(cpBuf); return VAR_RC(rc); } /* cleanup */ free(cpBuf); return VAR_OK; } /* format and expand a string */ var_rc_t var_format( var_t *var, char **dst_ptr, int force_expand, const char *fmt, ...) { var_rc_t rc; va_list ap; /* argument sanity checks */ if (var == NULL || dst_ptr == NULL || fmt == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); va_start(ap, fmt); rc = var_formatv(var, dst_ptr, force_expand, fmt, ap); va_end(ap); return VAR_RC(rc); } /* var_rc_t to string mapping table */ static const char *var_errors[] = { "everything ok", /* VAR_OK = 0 */ "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */ "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */ "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */ "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */ "invalid octal value", /* VAR_ERR_INVALID_OCTAL */ "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */ "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */ "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */ "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */ "out of memory", /* VAR_ERR_OUT_OF_MEMORY */ "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */ "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */ "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */ "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */ "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */ "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */ "invalid regex in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */ "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */ "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */ "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */ "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */ "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */ "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */ "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */ "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */ "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */ "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */ "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */ "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */ "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */ "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */ "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */ "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */ "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */ "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */ "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */ "index of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */ "index of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */ "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */ "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */ "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */ "invalid character in loop limits", /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */ "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */ "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */ "formatting failure" /* VAR_ERR_FORMATTING_FAILURE */ }; /* translate a return code into its corresponding descriptive text */ var_rc_t var_strerror(var_t *var, var_rc_t rc, char **str) { if (str == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); rc = (var_rc_t)(0 - rc); if ((int)rc < 0 || (int)rc >= (int)(sizeof(var_errors) / sizeof(char *))) *str = "unknown error"; else *str = (char *)var_errors[rc]; return VAR_OK; } @ 1.106 log @Bumped year in copyright messages for new year 2005 @ text @d2543 1 @ 1.105 log @o Various code comment cleanups. o Fixed dmalloc support. @ text @d3 3 a5 3 ** Copyright (c) 2001-2004 Ralf S. Engelschall ** Copyright (c) 2001-2004 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2004 Cable & Wireless (http://www.cw.com/) @ 1.104 log @flush more pending changes after some more platform checks @ text @d35 1 a47 1 #include "var.h" d49 1 a49 1 #include "dmalloc.h" d52 3 d2725 1 a2725 1 /* transslate a return code into its corresponding descriptive text */ @ 1.103 log @adjust copyrights @ text @d2470 1 a2470 1 fct = (var_cb_value_t)va_arg(ap, void *); d2479 1 a2479 1 fct = (var_cb_operation_t)va_arg(ap, void *); @ 1.102 log @Provide Autoconf check (AC_CHECK_VA_COPY) for va_copy(d,s) macro and fallback implementations and now that we can be sure that va_copy() exists for us, use it in var_formatv() and ts.c instead of the direct assignments (which are not sufficiently portable). @ text @d3 3 a5 3 ** Copyright (c) 2001-2003 Ralf S. Engelschall ** Copyright (c) 2001-2003 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2003 Cable & Wireless Deutschland (http://www.cw.com/de/) @ 1.101 log @correct spelling: privileges, convenient; @ text @d2624 1 a2624 1 apbak = ap; d2626 1 a2626 1 ap = apbak; @ 1.100 log @va_list is not comparable against NULL (import from http://cvs.openpkg.org/chngview?cn=11246) @ text @d2583 1 a2583 1 /* always NUL-terminate output for convinience reasons @ 1.99 log @final polishing for release 1.0.0 @ text @d129 1 a129 1 if (format == NULL || ap == NULL) d217 1 a217 1 if (format == NULL || ap == NULL) @ 1.98 log @add space to make code-style checkers happy, too @ text @d3 3 a5 3 ** Copyright (c) 2001-2002 Ralf S. Engelschall ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/) @ 1.97 log @allow a strict C++ compiler to compile this C code, too @ text @d1001 1 a1001 1 for (p = data->begin; p != data->end;) { @ 1.96 log @get rid of warning message because of constness @ text @d332 1 a332 1 if ((p = malloc(len + 1)) == NULL) d353 1 a353 1 if ((output->begin = output->end = malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL) d368 1 a368 1 if ((tmp = malloc(output->end - output->begin + len + 1)) == NULL) d384 1 a384 1 if ((new_buffer = realloc((char *)output->begin, new_size)) == NULL) d425 1 a425 1 char a, char b, char_class_t class) d428 1 a428 1 class[(int)a] = 1; d436 1 a436 1 const char *desc, char_class_t class) d442 1 a442 1 class[i] = 0; d449 1 a449 1 expand_range(desc[0], desc[2], class); d452 1 a452 1 class[(int) *desc] = 1; d915 1 a915 1 size_t i; d940 1 a940 1 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) { d1778 1 a1778 1 char operator; d1796 1 a1796 1 operator = *p++; d1802 1 a1802 1 if (operator == '+') d1808 1 a1808 1 operator = *p++; d1814 1 a1814 1 if (operator == '*') d1816 1 a1816 1 else if (operator == '/') { d1826 1 a1826 1 else if (operator == '%') { d2113 1 a2113 1 static var_rc_t d2236 1 a2236 1 static var_rc_t d2579 1 a2579 1 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0); d2727 2 a2728 2 rc = 0 - rc; if (rc < 0 || rc >= sizeof(var_errors) / sizeof(char *)) @ 1.95 log @fix final memory leak @ text @d1631 1 a1631 1 free(arg_ptr); d1638 1 a1638 1 free(arg_ptr); @ 1.94 log @fix one more memory leak and simplify tokenbuf operations @ text @d1629 3 a1631 1 if (rc < 0) d1633 1 d1637 2 @ 1.93 log @fix one more memory leak @ text @d802 1 a802 2 if (!tokenbuf_append (result, tmp.begin, tmp.end - tmp.begin)) { d806 1 d1094 1 a1094 1 if (!tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { d1218 1 a1218 1 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { d1229 1 a1229 1 if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) { d1244 1 a1244 1 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { d1257 1 a1257 1 if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) { d1265 1 a1265 1 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { @ 1.92 log @fix first set of memory leaks @ text @d751 1 a751 2 if (!tokenbuf_append (result, tmp.begin, tmp.end - tmp.begin)) { d755 1 @ 1.91 log @cosmetics: strip trailing whitespaces @ text @d705 1 d1940 1 @ 1.90 log @bugfix hex decoding @ text @d54 1 a54 1 d116 1 a116 1 static int d118 1 a118 1 int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, d122 1 a122 1 char ibuf[((sizeof(int)*8)/3)+10]; d193 1 a193 1 static int d195 1 a195 1 void *_ctx, d209 1 a209 1 static int d211 1 a211 1 char *buffer, size_t bufsize, d221 1 a221 1 if (buffer == NULL) d277 1 a277 1 static void d287 1 a287 1 static int d296 1 a296 1 static int d305 1 a305 1 static void d315 1 a315 1 static void d326 1 a326 1 static int d342 1 a342 1 static int d399 1 a399 1 static int d406 1 a406 1 static void d423 1 a423 1 static void d434 1 a434 1 static var_rc_t d465 1 a465 1 static int d475 1 a475 1 static var_rc_t d483 2 a484 2 if ( !expand_isoct(**src) || !expand_isoct((*src)[1]) d505 1 a505 1 static int d516 1 a516 1 static var_rc_t d524 1 a524 1 if ( !expand_ishex(**src) d550 1 a550 1 static var_rc_t d567 1 a567 1 static var_rc_t d580 1 a580 1 /* d592 1 a592 1 static int d611 1 a611 1 static int d630 1 a630 1 static int d652 1 a652 1 static int d670 1 a670 1 static int d717 1 a717 1 static int d768 1 a768 1 static int d819 1 a819 1 static int d825 1 a825 1 const char *p; d847 1 a847 1 static int d850 1 a850 1 const char *data, d852 1 a852 1 regmatch_t *pmatch, d904 1 a904 1 static int d907 1 a907 1 tokenbuf_t *data, d959 1 a959 1 static int d962 1 a962 1 tokenbuf_t *data, d964 1 a964 1 tokenbuf_t *replace, d1045 1 a1045 1 rc = regcomp(&preg, tmp.begin, d1068 2 a1069 2 else if ( multiline && (p + pmatch[0].rm_so) == mydata.end d1118 1 a1118 1 /* append prolog string and stop processing if we d1141 1 a1141 1 static int d1144 1 a1144 1 tokenbuf_t *data, d1146 1 a1146 1 int num2, d1183 1 a1183 1 static int d1186 2 a1187 2 tokenbuf_t *data, int width, d1284 1 a1284 1 static int d1306 1 a1306 1 static int d1588 1 a1588 1 size_t out_len; d1661 1 a1661 1 static int d1664 1 a1664 1 const char *begin, const char *end, d1724 1 a1724 1 else if ( var->syntax.index_mark != NUL d1765 1 a1765 1 static int d1768 1 a1768 1 const char *begin, const char *end, d1840 1 a1840 1 static int d1854 1 a1854 1 static int d1865 1 a1865 1 var_ptr, var_len, var_idx, d1871 1 a1871 1 has to proceed until all variables have undefined values. d1886 1 a1886 1 static int d1943 1 a1943 1 /* we must have the complete expanded variable name now, d2059 1 a2059 1 static int d2081 1 a2081 1 /* parse a simple variable name. d2106 1 a2106 1 static var_rc_t d2203 1 a2203 1 static int d2210 1 a2210 1 /* parse until delim_init (variable construct) d2221 1 a2221 1 && ( *p == var->syntax.index_open d2229 1 a2229 1 static var_rc_t d2250 2 a2251 2 if ( p != end && var->syntax.index_open != NUL d2271 4 a2274 4 ( ( open_stop && ( loop_limit_length < 0 || rel_lookup_cnt > ctx->rel_lookup_cnt)) || ( !open_stop d2281 1 a2281 1 /* open temporary context for recursion */ d2288 1 a2288 1 rc = parse_input(var, ctx, p, end, d2307 1 a2307 1 rc2 = parse_looplimits(var, ctx, p+rc+1, end, d2360 1 a2360 1 } d2390 1 a2390 1 /* d2397 1 a2397 1 var_rc_t d2414 1 a2414 1 var_rc_t d2425 1 a2425 1 var_rc_t d2427 2 a2428 2 var_t *var, var_config_t mode, d2453 1 a2453 1 if ( var->syntax_nameclass[(int)var->syntax.delim_init] d2486 1 a2486 1 var_rc_t d2488 3 a2490 3 var_t *var, const char *src, size_t srclen, char *dst, size_t dstlen, d2526 2 a2527 2 if ( end - src >= 3 && isdigit((int)src[1]) d2548 1 a2548 1 var_rc_t d2550 3 a2552 3 var_t *var, const char *src_ptr, size_t src_len, char **dst_ptr, size_t *dst_len, d2576 1 a2576 1 /* always NUL-terminate output for convinience reasons d2601 1 a2601 1 var_rc_t d2603 2 a2604 2 var_t *var, char **dst_ptr, int force_expand, d2645 1 a2645 1 var_rc_t d2647 2 a2648 2 var_t *var, char **dst_ptr, int force_expand, @ 1.89 log @polishing and copyright extension (add natural person) @ text @d530 1 a530 1 else if (c >= 'a' && c <= 'f') d532 1 a532 1 else if (c >= 'A' && c <= 'F') d545 1 a545 1 **dst = (char) c; @ 1.88 log @ops, remove left-over fixme hint @ text @d3 1 @ 1.87 log @Shit happens! Our tokenbuf_copy() function was a good idea, but cannot be used for the loop construct expansion, because an intermediate tokenbuf_append() can realloc(3) the underlying buffer, so the backuped token buffer would point to no longer valid data. We really have to re-introduce the original trick of Peter Simons where an offset is used only. @ text @a1061 1 /* XXX */ @ 1.86 log @New API functions var_format and var_formatv which provide a convinience interface to printf-style expansion and variable expansion. var_format is like a combination of snprintf(3) and var_expand(). Example: Assume the variable value context gives "bar" for ${ARRAY[7]}, then the call.. var_format(var, &tmp, 1, "foo${ARRAY[%d]}quux", 7);o ..results in tmp containing the malloc(3)'ed string "foobarquux". Thanks to Thomas L. for providing the hint to this functionality. @ text @d47 3 a314 10 tokenbuf_copy( tokenbuf_t *src, tokenbuf_t *dst) { dst->begin = src->begin; dst->end = src->end; dst->buffer_size = src->buffer_size; return; } static void d2240 1 a2240 1 tokenbuf_t output_backup; d2263 1 a2263 1 tokenbuf_copy(output, &output_backup); d2279 1 a2279 1 tokenbuf_copy(output, &output_backup); d2316 1 a2316 1 tokenbuf_copy(&output_backup, output); d2326 1 a2326 1 tokenbuf_copy(&output_backup, output); @ 1.85 log @Major bugfixing and enhancing of search & replace operation: - finally fix ${name:s/$/foo/} type substitutions (zero-length matching!) - add s/.../.../mg matching support (Perl-style multiline) - make non-multiline matching the default @ text @d107 128 d2607 65 d2718 2 a2719 1 "undefined operation" /* VAR_ERR_UNDEFINED_OPERATION */ @ 1.84 log @be even more carefully: regex(3) says rm_so _and_ rm_eo are -1 if a back-reference is not available. So we could match with "&&" here. But because we want to be very carefully we match also rm_eo for -1 and use "||" here. That is, we use a weaker condition to gain stronger safety. @ text @d848 1 d856 1 a856 1 for (p = flags->begin; p != flags->end; ++p) { d858 14 a871 11 case 'i': case_insensitive = 1; break; case 'g': global = 1; break; case 't': no_regex = 1; break; default: return VAR_ERR_UNKNOWN_REPLACE_FLAG; d923 4 a926 1 rc = regcomp(&preg, tmp.begin, REG_NEWLINE|REG_EXTENDED|((case_insensitive)?REG_ICASE:0)); d935 1 a935 1 for (p = mydata.begin; p != mydata.end; ) { d941 11 a951 1 if (rc != 0 || p + pmatch[0].rm_so == mydata.end) { d954 9 a962 1 } else { a970 8 /* append prolog string */ if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); tokenbuf_free(&myreplace); return VAR_ERR_OUT_OF_MEMORY; } d979 2 d982 4 a985 1 /* XXX??? */ d987 2 a992 1 tokenbuf_free(&myreplace); d997 2 a998 1 tokenbuf_free(&myreplace); @ 1.83 log @cleanup the code a little bit more before trying to fix it @ text @d760 1 a760 1 if (pmatch[i].rm_so == -1) { @ 1.82 log @One more bugfix to the search & replace stuff. Manual page regex(3) says: | Normally, regexec() returns 0 for success and the non-zero code | REG_NOMATCH for failure. Other non-zero error codes may be returned | in exceptional situations; see DIAGNOSTICS. So we have to check for "!= 0" and not just "== REG_NOMATCH" or we would perform substitutions with uninitialized data structures. @ text @d758 1 a758 1 i = *p - '0'; d845 1 d872 1 a872 1 tokenbuf_t tmp; a873 1 d880 1 a880 1 /* no match, copy character */ d885 1 a885 1 ++p; d887 3 a889 2 tokenbuf_append(&tmp, replace->begin, replace->end - replace->begin); p += search->end - search->begin; d891 1 a899 1 d903 1 a903 1 tokenbuf_t tmp; d910 1 a910 2 /* Copy the pattern and the data to our own buffer to make sure they're terminated with a null byte. */ d918 1 a918 1 /* Compile the pattern. */ d926 1 a926 1 /* Match the pattern and create the result string in the tmp buffer. */ d938 1 d946 2 a947 2 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so) || !tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { d953 6 a958 14 } else { p += pmatch[0].rm_eo; if (pmatch[0].rm_eo - pmatch[0].rm_so == 0) { if (!tokenbuf_append(&tmp, p, 1)) { regfree(&preg); tokenbuf_free(&tmp); tokenbuf_free(&mydata); tokenbuf_free(&myreplace); return VAR_ERR_OUT_OF_MEMORY; } ++p; } d960 13 d974 1 @ 1.81 log @Change semantics of ${name:-word} from "If $name is not empty string, then $name, else word" to: "If $name is not empty string and not undefined, then $name, else word". This provides a more intuitive usage of ${name:-word} because it allows the user to turn an undefined variable (as returned with VAR_ERR_UNDEFINED_VARIABLE by the callback) into a defined one. This is important because changing the callback to return undefined variables as empty variables is incorrect because it usually breaks the array loop construct. @ text @d933 2 a934 2 if (regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH || p + pmatch[0].rm_so == mydata.end) { @ 1.80 log @typo @ text @d155 18 d1273 3 a1275 1 if (data->begin != NULL && data->begin == data->end) { d1846 2 a1847 6 if (rc == VAR_ERR_UNDEFINED_VARIABLE) { if (ctx->force_expand) goto error_return; tokenbuf_set(result, begin - 1, p, 0); failed = 1; } d1860 1 d1881 10 @ 1.79 log @Fix a soon to occur bug in advance: a search and replace operation on an empty token buffer has to result again in an empty token buffer and not an undefined token buffer. I do not know why this worked and only crashes with the forthcoming changed ${name:-default} behaviour (because this is totally unrelated I would say). But nevertheless, this is a bug. So fix it, although it works before and after the fix as good as it could ;) @ text @d655 1 a655 1 /* try to parse substiution text */ @ 1.78 log @fix error description handling @ text @d252 2 a253 1 memcpy((char *)output->end, data, len); d909 1 @ 1.77 log @Fix regex problem by no longer allowing variables in the pattern part of s/pattern/subst/. Because it is far more common that one needs '$' there instead of a variable. @ text @a2421 2 "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */ "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */ d2463 3 a2465 1 "invalid character in loop limits" /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */ @ 1.76 log @Add new important feature: user-supplied post-operation functions. This allows one to configure a var_cb_operation_t callback in the var_t context and allow it to implement functions triggered by ${name:%func[(arg)]}. This is especially intended for application-specific encoding and post-adjustment functions. @ text @d450 19 d1305 1 a1305 1 rc = parse_substext_or_variable(var, ctx, p, end, &search); d1308 1 @ 1.75 log @Code Cleanup Phase V (too much details to describe) @ text @d75 6 a80 4 var_syntax_t syntax; char_class_t syntax_nameclass; var_cb_value_t cb_value_fct; void *cb_value_ctx; d86 4 a89 4 int force_expand; int rel_lookup_flag; int rel_lookup_cnt; int index_this; d448 1 d491 65 d1385 56 d2268 9 d2402 2 @ 1.74 log @Code Cleanup Phase IV (too much details to describe) @ text @d216 1 a216 1 /* Does the token contain text, but no buffer has been allocated yet? */ d218 1 a218 1 /* Check whether data borders to output. If, we can append d224 1 a224 1 /* OK, so copy the contents of output into an allocated buffer d235 1 a235 1 /* Does the token fit into the current buffer? If not, realloc a d249 1 a249 1 /* Append the data at the end of the current buffer. */ a273 15 static size_t tokenbuf_toint( tokenbuf_t *number) { const char *p; size_t num; num = 0; for (p = number->begin; p != number->end; ++p) { num *= 10; num += *p - '0'; } return num; } d276 1 a276 1 ** ==== ESCAPE SEQUENCE FUNCTIONS ==== d280 3 a282 1 static void expand_range(char a, char b, char_class_t class) d291 3 a293 1 static var_rc_t expand_character_class(const char *desc, char_class_t class) d297 1 a297 2 /* Clear the class array. */ d301 1 a301 3 /* Walk through the class description and set the appropriate entries in the array. */ d310 1 a310 1 ++desc; a312 1 d316 9 a324 1 static int isoct(char c) d332 3 a334 1 static var_rc_t expand_octal(const char **src, char **dst, const char *end) d340 3 a342 1 if (!isoct(**src) || !isoct((*src)[1]) || !isoct((*src)[2])) d349 1 a349 1 ++(*src); d353 1 a353 1 ++(*src); d358 1 a358 1 ++(*dst); d362 3 a364 1 static int ishex(char c) d373 3 a375 2 static var_rc_t expand_simple_hex(const char **src, char **dst, const char *end) d381 2 a382 1 if (!ishex(**src) || !ishex((*src)[1])) d393 1 a393 1 ++(*src); d403 1 a403 1 ++(*dst); d407 3 a409 2 static var_rc_t expand_grouped_hex(const char **src, char **dst, const char *end) d416 1 a416 1 ++(*src); d424 3 a426 1 static var_rc_t expand_hex(const char **src, char **dst, const char *end) d431 1 a431 1 ++(*src); a446 14 /* parse number */ static int parse_number( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse as long as digits are found */ for (p = begin; p != end && isdigit((int)(*p)); p++) ; return (p - begin); } d466 1 a466 1 /* parse expression? XXX text */ d488 1 a500 1 a502 1 d504 1 d516 1 d531 1 a531 1 return p - begin; d533 1 a533 1 error_return: d539 1 a551 1 a553 1 d555 1 d567 1 d582 1 a582 1 return p - begin; d584 1 a584 1 error_return: d590 1 d592 3 a594 1 parse_class_description(tokenbuf_t *src, tokenbuf_t *dst) d597 1 a597 1 const char *p = src->begin; d599 1 d618 1 a618 104 static int op_transpose( tokenbuf_t *data, tokenbuf_t *search, tokenbuf_t *replace) { tokenbuf_t srcclass, dstclass; const char *p; int rc; size_t i; tokenbuf_init(&srcclass); tokenbuf_init(&dstclass); if ((rc = parse_class_description(search, &srcclass)) != VAR_OK) goto error_return; if ((rc = parse_class_description(replace, &dstclass)) != VAR_OK) goto error_return; if (srcclass.begin == srcclass.end) { rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS; goto error_return; } if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) { rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH; goto error_return; } if (data->buffer_size == 0) { tokenbuf_t tmp; if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } tokenbuf_move(&tmp, data); } for (p = data->begin; p != data->end; ++p) { for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) { if (*p == srcclass.begin[i]) { *((char *)p) = dstclass.begin[i]; break; } } } tokenbuf_free(&srcclass); tokenbuf_free(&dstclass); return VAR_OK; error_return: tokenbuf_free(search); tokenbuf_free(replace); tokenbuf_free(&srcclass); tokenbuf_free(&dstclass); return rc; } static int op_cut_out_offset( tokenbuf_t *data, tokenbuf_t *number1, tokenbuf_t *number2, int isrange) { tokenbuf_t res; const char *p; size_t num1; size_t num2; num1 = tokenbuf_toint(number1); num2 = tokenbuf_toint(number2); /* Determine begin of result string. */ if ((data->end - data->begin) < num1) return VAR_ERR_OFFSET_OUT_OF_BOUNDS; else p = data->begin + num1; /* If num2 is zero, we copy the rest from there. */ if (num2 == 0) { if (!tokenbuf_assign(&res, p, data->end - p)) return VAR_ERR_OUT_OF_MEMORY; } else { /* OK, then use num2. */ if (isrange) { if ((p + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS; if (!tokenbuf_assign(&res, p, num2)) return VAR_ERR_OUT_OF_MEMORY; } else { if (num2 < num1) return VAR_ERR_OFFSET_LOGIC; if ((data->begin + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS; if (!tokenbuf_assign(&res, p, num2 - num1 + 1)) return VAR_ERR_OUT_OF_MEMORY; } } tokenbuf_free(data); tokenbuf_move(&res, data); return VAR_OK; } d621 1 d627 1 a627 1 const char *p = orig->begin; d630 1 d659 1 a659 1 pmatch[i].rm_eo - pmatch[i].rm_so)) { d675 56 d733 1 d770 1 a770 2 rc = strncasecmp(p, search->begin, search->end - search->begin); d772 1 a772 2 rc = strncmp(p, search->begin, search->end - search->begin); d781 1 a781 2 tokenbuf_append(&tmp, replace->begin, replace->end - replace->begin); d813 1 a813 1 rc = regcomp(&preg, tmp.begin, REG_NEWLINE | REG_EXTENDED|((case_insensitive)?REG_ICASE:0)); d831 1 a831 1 rc = parse_regex_replace(p, replace, pmatch, &myreplace); d839 1 a839 2 !tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { a871 1 d881 42 d926 1 d928 1 a928 1 tokenbuf_t *widthstr, a932 1 size_t width; a934 1 width = tokenbuf_toint(widthstr); a936 1 a937 1 d939 1 d944 1 a944 2 if (!tokenbuf_append (data, fill->begin, fill->end - fill->begin)) d948 1 a948 2 i = (width - (data->end - data->begin)) % (fill->end - fill->begin); d953 1 d958 1 a958 2 if (!tokenbuf_append (&result, fill->begin, fill->end - fill->begin)) { d964 1 a964 2 i = (width - (data->end - data->begin)) % (fill->end - fill->begin); d973 1 a973 1 d978 1 d981 1 a981 1 /* Create the prefix. */ d996 1 a996 2 /* Append the actual data string. */ d1001 1 a1001 2 /* Append the suffix. */ d1005 1 a1005 2 if (!tokenbuf_append (&result, fill->begin, fill->end - fill->begin)) { d1016 1 a1016 2 /* Move string from temporary buffer to data buffer. */ a1020 1 d1024 21 a1044 1 /* XXX */ d1057 1 d1114 1 a1114 1 rc = parse_number(var, ctx, p, end); d1119 2 a1120 1 tokenbuf_set(&number1, p, p + rc, 0); d1132 1 a1132 2 rc = parse_number(var, ctx, p, end); tokenbuf_set(&number2, p, p + rc, 0); d1135 1 a1135 1 rc = op_cut_out_offset(data, &number1, &number2, isrange); d1242 1 a1242 1 rc = op_search_and_replace(data, &search, &replace, &flags); d1273 1 a1273 1 rc = op_transpose(data, &search, &replace); d1285 1 a1285 1 rc = parse_number(var, ctx, p, end); a1289 1 tokenbuf_set(&number1, p, p + rc, 0); d1311 1 a1311 1 rc = op_padding(data, &number1, &replace, p[-1]); a1339 19 } /* parse an integer number ("123") */ static int parse_integer( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result) { const char *p; p = begin; (*result) = 0; while (isdigit(*p) && p != end) { (*result) *= 10; (*result) += (*p - '0'); p++; } return (p - begin); @ 1.73 log @more comments @ text @d143 3 a145 1 static void tokenbuf_init(tokenbuf_t *buf) d153 3 a155 1 static void tokenbuf_set(tokenbuf_t *buf, const char *begin, const char *end, size_t buffer_size) d163 3 a165 1 static void tokenbuf_copy(tokenbuf_t *src, tokenbuf_t *dst) d173 3 a175 1 static void tokenbuf_move(tokenbuf_t *src, tokenbuf_t *dst) d184 3 a186 1 static int tokenbuf_assign(tokenbuf_t *buf, const char *data, size_t len) d200 3 a202 1 static int tokenbuf_append(tokenbuf_t *output, const char *data, size_t len) d256 3 a258 1 static int tokenbuf_merge(tokenbuf_t *output, tokenbuf_t *input) d263 3 a265 1 static void tokenbuf_free(tokenbuf_t *buf) d274 3 a276 1 static size_t tokenbuf_toint(tokenbuf_t *number) d441 1 a441 42 static int parse_command (var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *data); static int parse_num_exp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed); /* parse plain text */ static int parse_text( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse until delim_init (variable construct) or index_open (loop construct) is found */ for (p = begin; p != end; p++) { if (*p == var->syntax.escape) { p++; /* skip next character */ if (p == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR; } else if (*p == var->syntax.delim_init) break; else if ( var->syntax.index_open != NUL && ( *p == var->syntax.index_open || *p == var->syntax.index_close)) break; } return (p - begin); } /* parse variable name */ static int parse_varname( var_t *var, var_parse_t *ctx, const char *begin, const char *end) { const char *p; /* parse as long as name class characters are found */ for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++) ; return (p - begin); } a497 1 /* convert a string into a decimal number */ d499 1 a499 15 convert_num_exp_read_int(const char **begin, const char *end) { int num = 0; do { num *= 10; num += **begin - '0'; ++(*begin); } while (isdigit(**begin) && *begin != end); return num; } /* parse numerical expression operand */ static int parse_num_exp_operand( d501 2 a502 3 const char *begin, const char *end, int *result, int *failed) d504 1 a504 1 const char *p; a506 1 var_parse_t myctx; d508 1 a508 1 p = begin; d512 1 a512 1 return VAR_ERR_INCOMPLETE_INDEX_SPEC; d514 2 a515 2 if (*p == '(') { rc = parse_num_exp(var, ctx, ++p, end, result, failed); d517 6 a522 27 return rc; p += rc; if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC; if (*p != ')') return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX; ++p; } else if (*p == var->syntax.delim_init) { ctx = var_parse_push(ctx, &myctx); ctx->force_expand = 1; rc = parse_variable(var, ctx, p, end, &tmp); ctx = var_parse_pop(ctx); if (rc == VAR_ERR_UNDEFINED_VARIABLE) { *failed = 1; ctx = var_parse_push(ctx, &myctx); ctx->force_expand = 0; rc = parse_variable(var, ctx, p, end, &tmp); ctx = var_parse_pop(ctx); if (rc < 0) return rc; p += rc; *result = 0; } else { if (rc < 0) return rc; a523 28 rc = parse_num_exp(var, ctx, tmp.begin, tmp.end, result, failed); tokenbuf_free(&tmp); if (rc < 0) return rc; } } else if (var->syntax.index_mark && *p == var->syntax.index_mark) { p++; *result = ctx->index_this; if (ctx->rel_lookup_flag) ctx->rel_lookup_cnt++; } else if (isdigit(*p)) { *result = convert_num_exp_read_int(&p, end); } else if (*p == '+') { if (end - p > 1 && isdigit(p[1])) { p++; *result = convert_num_exp_read_int(&p, end); } else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; } else if (*p == '-') { if (end - p > 1 && isdigit(p[1])) { p++; *result = convert_num_exp_read_int(&p, end); *result = 0 - *result; a524 5 else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; } else return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; d526 4 a529 30 return (p - begin); } /* parse numerical expression */ static int parse_num_exp( var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed) { const char *p; char operator; int right; int rc; p = begin; if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC; rc = parse_num_exp_operand(var, ctx, p, end, result, failed); if (rc < 0) return rc; p += rc; while (p != end) { if (*p == '+' || *p == '-') { operator = *p++; rc = parse_num_exp(var, ctx, p, end, &right, failed); if (rc < 0) return rc; d531 4 a534 33 if (operator == '+') *result = *result + right; else *result = *result - right; } else if (*p == '*' || *p == '/' || *p == '%') { operator = *p++; rc = parse_num_exp_operand(var, ctx, p, end, &right, failed); if (rc < 0) return rc; p += rc; if (operator == '*') { *result = *result * right; } else if (operator == '/') { if (right == 0) { if (*failed) *result = 0; else return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; } else *result = *result / right; } else if (operator == '%') { if (right == 0) { if (*failed) *result = 0; else return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; } else *result = *result % right; d537 3 a539 3 else break; } a540 30 } /* lookup a variable value by callin the callback function */ static int lookup_value( var_t *var, var_parse_t *ctx, const char *var_ptr, size_t var_len, int var_idx, const char **val_ptr, size_t *val_len, size_t *val_size) { char buf[1]; int rc; /* pass through to original callback */ rc = (*var->cb_value_fct)(var, var->cb_value_ctx, var_ptr, var_len, var_idx, val_ptr, val_len, val_size); /* convert undefined variable into empty variable if relative lookups are counted. This is the case inside an active loop construct if no limits are given. There the parse_input() has to proceed until all variables have undefined values. This trick here allows it to determine this case. */ if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) { ctx->rel_lookup_cnt--; buf[0] = NUL; *val_ptr = buf; *val_len = 0; *val_size = 0; return VAR_OK; } d542 3 d549 1 a549 1 parse_expression( d555 1 a555 3 const char *data; size_t len, buffer_size; int failed = 0; a556 3 int idx = 0; tokenbuf_t name; tokenbuf_t tmp; d558 1 a558 3 /* Clear the tokenbufs to make sure we have a defined state. */ tokenbuf_init(&name); a559 1 tokenbuf_init(result); d561 1 a561 3 /* Expect STARTDELIM. */ if (p == end || *p != var->syntax.delim_open) a563 6 if (++p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; /* Get the name of the variable to expand. The name may consist of an arbitrary number of VARNAMEs and VARIABLEs. */ d565 1 a565 1 rc = parse_varname(var, ctx, p, end); d569 1 a569 1 if (!tokenbuf_append(&name, p, rc)) { d580 3 a582 1 if (!tokenbuf_append(&name, tmp.begin, tmp.end - tmp.begin)) { a585 1 p += rc; d589 8 a596 2 /* We must have the complete variable name now, so make sure we do. */ d598 2 a599 285 if (name.begin == name.end) { if (ctx->force_expand) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } else { /* If no force_expand is requested, we have to back-off. We're not sure whether our approach here is 100% correct, because it _could_ have side-effects according to Peter Simons, but as far as we know and tried it, it is correct. But be warned -- RSE */ result->begin = begin - 1; result->end = p; result->buffer_size = 0; goto goahead; } } /* If the next token is START-INDEX, read the index specification. */ if (var->syntax.index_open && *p == var->syntax.index_open) { rc = parse_num_exp(var, ctx, ++p, end, &idx, &failed); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; goto error_return; } p += rc; if (p == end) { rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; goto error_return; } if (*p != var->syntax.index_close) { rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; goto error_return; } p++; } /* Now we have the name of the variable stored in "name". The next token here must either be an END-DELIM or a ':'. */ if (p == end || (*p != var->syntax.delim_close && *p != ':')) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } p++; /* Use the lookup callback to get the variable's contents. */ if (failed) { result->begin = begin - 1; result->end = p; result->buffer_size = 0; } else { rc = lookup_value(var, ctx, name.begin, name.end - name.begin, idx, &data, &len, &buffer_size); if (rc == VAR_ERR_UNDEFINED_VARIABLE) { /* The variable is undefined. What we'll do now depends on the force_expand flag. */ if (ctx->force_expand) goto error_return; /* Initialize result to point back to the original text in the buffer. */ result->begin = begin - 1; result->end = p; result->buffer_size = 0; failed = 1; } else if (rc < 0 /* != VAR_OK */) { goto error_return; } else { /* The preliminary result is the contents of the variable. This may be modified by the commands that may follow. */ result->begin = data; result->end = data + len; result->buffer_size = buffer_size; } } goahead: if (p[-1] == ':') { /* Parse and execute commands. */ tokenbuf_free(&tmp); p--; while (p != end && *p == ':') { p++; if (!failed) rc = parse_command(var, ctx, p, end, result); else rc = parse_command(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; p += rc; if (failed) result->end += rc; } if (p == end || *p != var->syntax.delim_close) { rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } p++; if (failed) result->end++; } /* Exit gracefully. */ tokenbuf_free(&name); tokenbuf_free(&tmp); return p - begin; /* Exit in case of an error. */ error_return: tokenbuf_free(&name); tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } static int parse_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p = begin; const char *data; size_t len, buffer_size; int rc, rc2; /* Clear the result tokenbuf to make sure we're in a defined state. */ tokenbuf_init(result); /* Expect VARINIT. */ if (p == end || *p != var->syntax.delim_init) return 0; if (++p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; /* Try to read the variable name. If that fails, we're parsing a complex expression. */ rc = parse_varname(var, ctx, p, end); if (rc < 0) return rc; if (rc > 0) { rc2 = lookup_value(var, ctx, p, rc, 0, &data, &len, &buffer_size); if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) { result->begin = begin; result->end = begin + 1 + rc; result->buffer_size = 0; return 1 + rc; } if (rc2 < 0 /* != VAR_OK */) return rc2; result->begin = data; result->end = data + len; result->buffer_size = buffer_size; return 1 + rc; } /* OK, we're dealing with a complex expression here. */ rc = parse_expression(var, ctx, p, end, result); if (rc > 0) rc++; return rc; } static int parse_exptext_or_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p = begin; tokenbuf_t tmp; int rc; tokenbuf_init(result); tokenbuf_init(&tmp); if (begin == end) return 0; do { rc = parse_exptext(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(result, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { p += rc; if (!tokenbuf_append (result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } } } while (rc > 0); tokenbuf_free(&tmp); return p - begin; error_return: tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } static int parse_substext_or_variable( var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result) { const char *p = begin; tokenbuf_t tmp; int rc; tokenbuf_init(result); tokenbuf_init(&tmp); if (begin == end) return 0; do { rc = parse_substext(var, ctx, p, end); if (rc < 0) goto error_return; if (rc > 0) { if (!tokenbuf_append(result, p, rc)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } p += rc; } rc = parse_variable(var, ctx, p, end, &tmp); if (rc < 0) goto error_return; if (rc > 0) { p += rc; if (!tokenbuf_append (result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_ERR_OUT_OF_MEMORY; goto error_return; } } } while (rc > 0); tokenbuf_free(&tmp); return p - begin; error_return: tokenbuf_free(&tmp); tokenbuf_free(result); return rc; } static int parse_class_description(tokenbuf_t *src, tokenbuf_t *dst) a695 1 a701 1 a855 1 a863 1 d871 1 a871 3 /* Match the pattern and create the result string in the tmp buffer. */ d934 1 d943 1 a943 1 size_t width = tokenbuf_toint(widthstr); d946 1 a996 1 a1012 1 a1018 1 a1035 1 d1044 3 d1048 1 a1048 1 parse_command( d1053 1 a1053 1 const char *p = begin; d1059 1 d1061 1 d1068 2 a1069 2 if (begin == end) d1072 1 d1074 4 a1077 4 case 'l': /* Turn data to lowercase. */ if (data->begin) { char *ptr; /* If the buffer does not live in an allocated buffer, a1078 1 d1085 3 a1087 2 for (ptr = (char *)data->begin; ptr != data->end; ++ptr) *ptr = tolower(*ptr); d1091 6 a1096 4 case 'u': /* Turn data to uppercase. */ if (data->begin) { char *ptr; d1098 1 a1098 2 if (!tokenbuf_assign (data, data->begin, data->end - data->begin)) { d1103 3 a1105 2 for (ptr = (char *) data->begin; ptr != data->end; ++ptr) *ptr = toupper(*ptr); d1107 1 a1107 1 ++p; d1109 4 a1112 3 case 'o': /* Cut out substrings. */ ++p; d1118 1 a1118 3 number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; a1119 1 d1122 1 a1122 1 ++p; d1125 1 a1125 1 ++p; a1129 1 d1131 1 a1131 3 number2.begin = p; number2.end = p + rc; number2.buffer_size = 0; d1133 1 a1133 1 if (data->begin) { d1139 4 a1142 3 case '#': /* Substitute length of the string. */ if (data->begin) { d1151 1 a1151 1 ++p; d1153 3 a1155 2 case '-': /* Substitute parameter if data is empty. */ d1170 3 a1172 2 case '*': /* Return "" if data is not empty, parameter otherwise. */ d1193 3 a1195 2 case '+': /* Substitute parameter if data is not empty. */ d1210 3 a1212 2 case 's': /* Search and replace. */ a1213 1 a1216 1 a1220 1 a1225 1 a1229 1 a1234 1 d1238 1 a1238 3 flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; d1240 1 a1240 2 if (data->begin) { d1246 3 a1248 2 case 'y': /* Transpose characters from class A to class B. */ a1249 1 a1252 1 a1256 1 a1261 1 a1265 1 d1270 1 a1270 2 ++p; d1277 3 a1279 3 case 'p': /* Padding. */ a1280 1 a1283 1 d1289 1 a1289 3 number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; a1290 1 a1295 1 a1299 1 a1304 1 a1309 1 d1316 1 a1316 1 d1321 1 a1321 2 /* Exit gracefully. */ d1328 1 a1328 1 return p - begin; d1330 2 a1331 1 error_return: d1342 457 d1802 1 a1802 2 var_t *var, var_parse_t *ctx, d1824 1 a1824 1 rc = parse_num_exp(var, ctx, p, end, start, &failed); d1841 1 a1841 1 rc = parse_num_exp(var, ctx, p, end, step, &failed); d1873 1 a1873 1 rc = parse_num_exp(var, ctx, p, end, stop, &failed); d1893 26 @ 1.72 log @Code Cleanup Phase III (too much details to describe) @ text @d2099 1 d2116 1 d2127 1 d2179 1 d2241 1 d2294 1 d2341 1 @ 1.71 log @Code Cleanup Phase II (too much details to describe) @ text @d151 16 d244 5 d535 1 a535 1 /* parse number expression operand */ d623 1 d630 1 a630 1 const char *p = begin; d635 2 a636 1 if (begin == end) d692 1 a692 1 /* callback wrapper function */ d707 5 a711 1 /* convert undefined variable into empty variable */ d1833 1 a1833 1 /* expand the loop limits */ d1839 1 a1839 2 int *start, int *step, int *stop, int *open_end) d1845 1 d1847 3 a1849 1 if (begin == end) d1851 2 d1855 1 a1855 2 else p++; d1857 1 a1857 1 /* Read start value for the loop. */ d1861 1 a1861 1 *start = 0; /* use default */ d1869 1 d1872 1 a1872 2 else p++; d1874 1 a1874 1 /* Read step value for the loop. */ d1878 1 a1878 1 *step = 1; /* use default */ d1885 2 d1888 1 d1891 12 a1902 10 else { p++; *stop = *step; *step = 1; if (rc > 0) *open_end = 0; else *open_end = 1; return p - begin; } d1904 1 a1904 2 else p++; d1906 1 a1906 1 /* Read stop value for the loop. */ d1910 2 a1911 2 *stop = 0; /* use default */ *open_end = 1; d1916 1 a1916 1 *open_end = 0; d1921 2 d1925 1 d1927 2 a1928 1 return ++p - begin; d1934 1 a1934 2 var_t *var, var_parse_t *ctx, d1936 1 a1936 2 tokenbuf_t *output, int recursion_level) d1938 1 a1938 1 const char *p = begin; d1941 1 a1941 1 int start, step, stop, open_end; d1943 1 a1943 1 int output_backup; d1948 2 a1949 1 tokenbuf_init(&result); d1953 1 a1953 1 if ( begin != end d1955 2 a1956 1 && *begin == var->syntax.index_open) { d1958 1 d1960 3 a1962 1 begin++; d1966 1 a1966 4 open_end = 1; rc = 0; output_backup = 0; rel_lookup_cnt = ctx->rel_lookup_cnt; d1968 4 d1974 1 a1974 1 ( ( open_end d1977 2 a1978 2 || ( !open_end && i <= stop) ); d1980 3 a1982 2 /* remember current state for backing off */ output_backup = output->end - output->begin; d1990 2 a1991 1 rc = parse_input(var, ctx, begin, end, d1998 1 d2001 3 a2003 1 if (begin[rc] != var->syntax.index_close) { d2007 2 d2010 2 a2011 3 rc2 = parse_looplimits(var, ctx, begin + rc + 1, end, &start, &step, &stop, &open_end); d2018 2 a2019 1 output->end = output->begin + output_backup; d2024 12 a2035 7 if (open_end) output->end = output->begin + output_backup; else rel_lookup_cnt = ctx->rel_lookup_cnt; begin += rc; begin++; begin += loop_limit_length; d2040 1 a2040 1 rc = parse_text(var, ctx, begin, end); d2042 1 a2042 1 if (!tokenbuf_append(output, begin, rc)) { d2046 1 a2046 1 begin += rc; d2052 2 a2053 1 rc = parse_variable(var, ctx, begin, end, &result); d2055 2 a2056 1 if (!tokenbuf_append(output, result.begin, result.end - result.begin)) { d2060 2 a2061 1 begin += rc; d2063 2 a2064 1 } d2068 1 a2068 1 } while (begin != end && rc > 0); d2070 7 a2076 1 if (recursion_level == 0 && begin != end) { d2081 2 a2082 1 return begin - p; d2084 4 a2087 1 error_return: d2089 1 a2089 4 tokenbuf_free(&result); output->begin = p; output->end = begin; output->buffer_size = 0; d2253 2 a2254 2 ctx.lower = NULL; ctx.force_expand = force_expand; d2256 2 a2257 2 ctx.rel_lookup_cnt = 0; ctx.index_this = 0; d2259 1 a2259 5 /* set the dst_ptr pointer to the src_ptr so that it is correctly initialized in case we fail with an error later. */ *dst_ptr = (char *)src_ptr; /* call the parsing */ d2261 1 a2261 1 rc = parse_input(var, &ctx, src_ptr, src_ptr + src_len, &output, 0); d2263 1 a2263 1 /* post-process output */ d2265 2 a2266 1 /* always NUL-terminate output for convinience reasons */ d2273 1 a2273 1 /* provide dst_ptrs */ d2276 1 a2276 3 *dst_len = output.end - output.begin; /* canonify all positive answers */ d2280 1 a2280 1 /* provide error dst_ptr */ d2283 1 a2283 1 *dst_len = output.end - output.begin; @ 1.70 log @Code Cleanup Phase I (too much details to describe) @ text @d82 8 a89 3 typedef struct { int force_expand; } var_parse_t; d105 26 d262 1 d401 3 a403 3 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, int force_expand, tokenbuf_t *result, int index_this, int* rel_lookup_flag); static int parse_command (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int force_expand, tokenbuf_t *data, int index_this, int* rel_lookup_flag); static int parse_num_exp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int index_this, int* result, int* failed, int* rel_lookup_flag); d519 2 a520 1 int index_this, int *result, int *failed, int *rel_lookup_flag) d525 1 d534 1 a534 2 rc = parse_num_exp(var, ctx, ++p, end, index_this, result, failed, rel_lookup_flag); d545 4 a548 2 rc = parse_variable(var, ctx, p, end, 1, &tmp, index_this, rel_lookup_flag); d551 4 a554 2 rc = parse_variable(var, ctx, p, end, 0, &tmp, index_this, rel_lookup_flag); d564 1 a564 2 rc = parse_num_exp(var, ctx, tmp.begin, tmp.end, index_this, result, failed, rel_lookup_flag ); d572 3 a574 2 *result = index_this; (*rel_lookup_flag)++; d606 1 a606 2 int index_this, int *result, int *failed, int *rel_lookup_flag) d616 1 a616 2 rc = parse_num_exp_operand(var, ctx, p, end, index_this, result, failed, rel_lookup_flag); d624 1 a624 2 rc = parse_num_exp(var, ctx, p, end, index_this, &right, failed, rel_lookup_flag); d635 1 a635 2 rc = parse_num_exp_operand(var, ctx, p, end, index_this, &right, failed, rel_lookup_flag); d669 28 d701 1 a701 2 int force_expand, tokenbuf_t *result, int index_this, int *rel_lookup_flag) d741 1 a741 2 rc = parse_variable(var, ctx, p, end, force_expand, &tmp, index_this, rel_lookup_flag); d757 1 a757 1 if (force_expand) { d777 1 a777 2 rc = parse_num_exp(var, ctx, ++p, end, index_this, &idx, &failed, rel_lookup_flag); d814 2 a815 2 rc = (*var->cb_value_fct) (var, var->cb_value_ctx, name.begin, name.end - name.begin, idx, &data, &len, &buffer_size); d819 1 a819 1 if (force_expand) d850 1 a850 3 rc = parse_command(var, ctx, p, end, force_expand, result, index_this, rel_lookup_flag); d852 1 a852 3 rc = parse_command(var, ctx, p, end, force_expand, &tmp, index_this, rel_lookup_flag); d889 1 a889 2 int force_expand, tokenbuf_t *result, int index_this, int *rel_lookup_flag) d916 2 a917 2 rc2 = (*var->cb_value_fct)(var, var->cb_value_ctx, p, rc, 0, &data, &len, &buffer_size); if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !force_expand) { d933 1 a933 2 rc = parse_expression(var, ctx, p, end, force_expand, result, index_this, rel_lookup_flag); d943 1 a943 3 int force_expand, tokenbuf_t *result, int index_this, int *rel_lookup_flag) d967 1 a967 2 rc = parse_variable(var, ctx, p, end, force_expand, &tmp, index_this, rel_lookup_flag); d993 1 a993 3 int force_expand, tokenbuf_t *result, int index_this, int *rel_lookup_flag) d1017 1 a1017 2 rc = parse_variable(var, ctx, p, end, force_expand, &tmp, index_this, rel_lookup_flag); d1039 2 a1040 1 static int parse_class_description(tokenbuf_t *src, tokenbuf_t *dst) d1063 5 a1067 2 static int transpose(tokenbuf_t *data, tokenbuf_t *search, tokenbuf_t *replace) d1103 1 a1103 1 *((char *) p) = dstclass.begin[i]; d1121 6 a1126 2 static int cut_out_offset(tokenbuf_t *data, tokenbuf_t *number1, tokenbuf_t *number2, int isrange) d1169 6 a1174 2 static int parse_regex_replace(const char *data, tokenbuf_t *orig, regmatch_t *pmatch, tokenbuf_t *expanded) d1223 6 a1228 2 static int search_and_replace(tokenbuf_t *data, tokenbuf_t *search, tokenbuf_t *replace, tokenbuf_t *flags) d1381 6 a1386 2 static int padding(tokenbuf_t *data, tokenbuf_t *widthstr, tokenbuf_t *fill, char position) d1497 1 a1497 2 int force_expand, tokenbuf_t *data, int index_this, int *rel_lookup_flag) d1517 33 a1549 5 case 'l': /* Turn data to lowercase. */ if (data->begin) { char *ptr; /* If the buffer does not live in an allocated buffer, we have to copy it before modifying the contents. */ d1551 31 a1581 3 if (data->buffer_size == 0) { if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) { rc = VAR_ERR_OUT_OF_MEMORY; a1582 1 } d1584 1 a1584 5 for (ptr = (char *)data->begin; ptr != data->end; ++ptr) *ptr = tolower(*ptr); } p++; break; d1586 6 a1591 6 case 'u': /* Turn data to uppercase. */ if (data->begin) { char *ptr; if (data->buffer_size == 0) { if (!tokenbuf_assign (data, data->begin, data->end - data->begin)) { a1595 20 for (ptr = (char *) data->begin; ptr != data->end; ++ptr) *ptr = toupper(*ptr); } ++p; break; case 'o': /* Cut out substrings. */ ++p; rc = parse_number(var, ctx, p, end); if (rc == 0) { rc = VAR_ERR_MISSING_START_OFFSET; goto error_return; } number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; if (*p == ',') { isrange = 0; d1597 1 a1597 7 } else if (*p == '-') { isrange = 1; ++p; } else { rc = VAR_ERR_INVALID_OFFSET_DELIMITER; goto error_return; } d1599 3 a1601 7 rc = parse_number(var, ctx, p, end); number2.begin = p; number2.end = p + rc; number2.buffer_size = 0; p += rc; if (data->begin) { rc = cut_out_offset(data, &number1, &number2, isrange); d1604 10 a1613 2 } break; d1615 7 a1621 7 case '#': /* Substitute length of the string. */ if (data->begin) { char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */ sprintf(buf, "%d", (int)(data->end - data->begin)); tokenbuf_free(data); if (!tokenbuf_assign(data, buf, strlen(buf))) { rc = VAR_ERR_OUT_OF_MEMORY; d1624 12 a1635 3 } ++p; break; d1637 11 a1647 31 case '-': /* Substitute parameter if data is empty. */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, force_expand, &tmptokbuf, index_this, rel_lookup_flag); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (data->begin != NULL && data->begin == data->end) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } break; case '*': /* Return "" if data is not empty, parameter otherwise. */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, force_expand, &tmptokbuf, index_this, rel_lookup_flag); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (data->begin != NULL) { if (data->begin == data->end) { a1649 4 } else { tokenbuf_free(data); data->begin = data->end = ""; data->buffer_size = 0; d1651 1 a1651 2 } break; d1653 2 a1654 16 case '+': /* Substitute parameter if data is not empty. */ p++; rc = parse_exptext_or_variable(var, ctx, p, end, force_expand, &tmptokbuf, index_this, rel_lookup_flag); if (rc < 0) goto error_return; if (rc == 0) { rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } p += rc; if (data->begin != NULL && data->begin != data->end) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } break; d1656 3 a1658 2 case 's': /* Search and replace. */ p++; d1660 4 a1663 3 if (*p != '/') return VAR_ERR_MALFORMATTED_REPLACE; p++; d1665 5 a1669 5 rc = parse_substext_or_variable(var, ctx, p, end, force_expand, &search, index_this, rel_lookup_flag); if (rc < 0) goto error_return; p += rc; d1671 4 a1674 5 if (*p != '/') { rc = VAR_ERR_MALFORMATTED_REPLACE; goto error_return; } p++; d1676 5 a1680 11 rc = parse_substext_or_variable(var, ctx, p, end, force_expand, &replace, index_this, rel_lookup_flag); if (rc < 0) goto error_return; p += rc; if (*p != '/') { rc = VAR_ERR_MALFORMATTED_REPLACE; goto error_return; } p++; d1682 1 a1682 10 rc = parse_exptext(var, ctx, p, end); if (rc < 0) goto error_return; flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; p += rc; if (data->begin) { rc = search_and_replace(data, &search, &replace, &flags); d1685 4 a1688 2 } break; d1690 6 a1695 2 case 'y': /* Transpose characters from class A to class B. */ p++; d1697 2 a1698 3 if (*p != '/') return VAR_ERR_MALFORMATTED_TRANSPOSE; p++; d1700 3 a1702 5 rc = parse_substext_or_variable(var, ctx, p, end, force_expand, &search, index_this, rel_lookup_flag); if (rc < 0) goto error_return; p += rc; d1704 4 a1707 5 if (*p != '/') { rc = VAR_ERR_MALFORMATTED_TRANSPOSE; goto error_return; } p++; d1709 5 a1713 5 rc = parse_substext_or_variable(var, ctx, p, end, force_expand, &replace, index_this, rel_lookup_flag); if (rc < 0) goto error_return; p += rc; d1715 4 a1718 5 if (*p != '/') { rc = VAR_ERR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; d1720 2 a1721 3 if (data->begin) { rc = transpose(data, &search, &replace); if (rc < 0) d1723 2 a1724 2 } break; d1726 6 a1732 2 case 'p': /* Padding. */ p++; d1734 2 a1735 3 if (*p != '/') return VAR_ERR_MALFORMATTED_PADDING; p++; d1737 3 a1739 9 rc = parse_number(var, ctx, p, end); if (rc == 0) { rc = VAR_ERR_MISSING_PADDING_WIDTH; goto error_return; } number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; d1741 9 a1749 5 if (*p != '/') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; d1751 5 a1755 5 rc = parse_substext_or_variable(var, ctx, p, end, force_expand, &replace, index_this, rel_lookup_flag); if (rc < 0) goto error_return; p += rc; d1757 4 a1760 5 if (*p != '/') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; d1762 5 a1766 5 if (*p != 'l' && *p != 'c' && *p != 'r') { rc = VAR_ERR_MALFORMATTED_PADDING; goto error_return; } p++; d1768 2 a1769 3 if (data->begin) { rc = padding(data, &number1, &replace, p[-1]); if (rc < 0) d1771 9 a1779 2 } break; d1781 2 a1782 2 default: return VAR_ERR_UNKNOWN_COMMAND_CHAR; d1812 2 a1813 1 int* start, int* step, int* stop, int* open_end) a1817 1 int dummy; d1829 1 a1829 1 rc = parse_num_exp(var, ctx, p, end, 0, start, &failed, &dummy); d1846 1 a1846 1 rc = parse_num_exp(var, ctx, p, end, 0, step, &failed, &dummy); d1874 1 a1874 1 rc = parse_num_exp(var, ctx, p, end, 0, stop, &failed, &dummy); a1892 36 /* callback wrapper context */ typedef struct { var_cb_value_t cb_value_fct; void *cb_value_ctx; int *rel_lookup_flag; } var_wrapper_t; /* callback wrapper function */ static int lookup_wrapper( var_t *var, void *ctx, const char *var_ptr, size_t var_len, int var_idx, const char **val_ptr, size_t *val_len, size_t *val_size) { char buf[1]; var_wrapper_t *wcon = (var_wrapper_t *)ctx; int rc; /* pass through to original callback */ rc = (*wcon->cb_value_fct)(var, wcon->cb_value_ctx, var_ptr, var_len, var_idx, val_ptr, val_len, val_size); /* convert undefined variable into empty variable */ if (rc == VAR_ERR_UNDEFINED_VARIABLE) { (*wcon->rel_lookup_flag)--; buf[0] = NUL; *val_ptr = buf; *val_len = 0; *val_size = 0; return VAR_OK; } return rc; } d1899 2 a1900 3 int force_expand, tokenbuf_t *output, int index_this, size_t recursion_level, int *rel_lookup_flag) d1908 1 a1908 3 var_wrapper_t wcon; int my_rel_lookup_flag; int original_rel_lookup_state; d1910 1 d1914 5 a1918 4 if (rel_lookup_flag == NULL) { rel_lookup_flag = &my_rel_lookup_flag; *rel_lookup_flag = 0; } a1919 3 do { if (begin != end && var->syntax.index_open && *begin == var->syntax.index_open) { original_rel_lookup_state = *rel_lookup_flag; d1928 3 a1930 1 re_loop: d1932 5 a1936 2 (open_end && (loop_limit_length < 0 || *rel_lookup_flag > original_rel_lookup_state)) || (!open_end && i <= stop); d1938 1 a1938 1 *rel_lookup_flag = original_rel_lookup_state; d1941 5 a1945 6 /* activate callback wrapper */ wcon.cb_value_fct = var->cb_value_fct; wcon.cb_value_ctx = var->cb_value_ctx; wcon.rel_lookup_flag = rel_lookup_flag; var->cb_value_fct = lookup_wrapper; var->cb_value_ctx = &wcon; d1948 1 a1948 1 1, output, i, recursion_level+1, rel_lookup_flag); d1950 3 a1952 3 /* deactivate callback wrapper */ var->cb_value_fct = wcon.cb_value_fct; var->cb_value_ctx = wcon.cb_value_ctx; d1978 1 a1978 1 *rel_lookup_flag = original_rel_lookup_state; d1985 1 d1997 2 a1998 3 rc = parse_variable(var, ctx, begin, end, force_expand, &result, index_this, rel_lookup_flag); d2009 1 d2188 1 d2190 3 d2200 1 a2200 3 rc = parse_input(var, &ctx, src_ptr, src_ptr + src_len, ctx.force_expand, &output, 0, 0, NULL); @ 1.69 log @prepare for API finally passing "val_t *" to callback, currently pass NULL @ text @d61 10 d84 1 a84 1 } var_expand_t; d98 5 a102 1 /* Routines for manipulation of token buffers. */ d139 1 a139 1 *((char *)(buf->end)) = '\0'; d193 1 a193 1 *((char *)output->end) = '\0'; d219 5 a223 1 /* Routines for the expansion of quoted-pair expressions. */ d245 2 a246 2 while (*desc != '\0') { if (desc[1] == '-' && desc[2] != '\0') { d362 16 a377 19 /* The recursive-descent parser for variable expressions. */ static int variable(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int index_mark, int* rel_lookup_flag); static int command(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *data, int index_mark, int* rel_lookup_flag); static int num_exp(const char *begin, const char *end, int index_mark, int* result, int* failed, int* rel_lookup_flag, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void* lookup_context); static int text(const char *begin, const char *end, char delim_init, char index_open, char index_close, char escape) d381 6 a386 3 for (p = begin; p != end; ++p) { if (*p == escape) { if (++p == end) d389 1 a389 1 else if (*p == delim_init) d391 3 a393 1 else if (index_open && (*p == index_open || *p == index_close)) d396 1 a396 1 return p - begin; d399 5 a403 2 static int varname(const char *begin, const char *end, const char_class_t nameclass) d407 2 a408 1 for (p = begin; p != end && nameclass[(int) *p]; p++) d410 1 a410 1 return p - begin; d413 5 a417 1 static int number(const char *begin, const char *end) d421 2 a422 1 for (p = begin; p != end && isdigit((int)*p); p++) d424 1 a424 1 return p - begin; d427 5 a431 2 static int substext(const char *begin, const char *end, const var_syntax_t *config) d435 3 a437 2 for (p = begin; p != end && *p != config->delim_init && *p != '/'; p++) { if (*p == config->escape) { d443 1 a443 1 return p - begin; d446 5 a450 2 static int exptext(const char *begin, const char *end, const var_syntax_t *config) d454 6 a459 6 for (p = begin; p != end && *p != config->delim_init && *p != config->delim_close && *p != ':'; p++) { if (*p == config->escape) { d465 1 a465 1 return p - begin; d468 3 a470 1 static int num_exp_read_int(const char **begin, const char *end) d482 6 a487 5 static int num_exp_read_operand(const char *begin, const char *end, int index_mark, int *result, int *failed, int *rel_lookup_flag, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context) d489 1 a489 1 const char* p = begin; d493 1 d500 2 a501 2 rc = num_exp(++p, end, index_mark, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d511 3 a513 3 else if (*p == config->delim_init) { rc = variable(p, end, config, nameclass, lookup, lookup_context, 1, &tmp, index_mark, rel_lookup_flag); d516 2 a517 2 rc = variable(p, end, config, nameclass, lookup, lookup_context, 0, &tmp, index_mark, rel_lookup_flag); d527 2 a528 2 rc = num_exp(tmp.begin, tmp.end, index_mark, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d534 1 a534 1 else if (config->index_mark && *p == config->index_mark) { d536 1 a536 1 *result = index_mark; d540 1 a540 1 *result = num_exp_read_int(&p, end); d545 1 a545 1 *result = num_exp_read_int(&p, end); d553 1 a553 1 *result = num_exp_read_int(&p, end); d562 1 a562 1 return p - begin; d565 6 a570 5 static int num_exp(const char *begin, const char *end, int index_mark, int *result, int *failed, int *rel_lookup_flag, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context) d580 2 a581 3 rc = num_exp_read_operand(p, end, index_mark, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d589 2 a590 2 rc = num_exp(p, end, index_mark, &right, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d601 2 a602 2 rc = num_exp_read_operand(p, end, index_mark, &right, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d636 7 a642 6 static int expression(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int index_mark, int *rel_lookup_flag) { d660 1 a660 1 if (p == end || *p != config->delim_open) d670 1 a670 1 rc = varname(p, end, nameclass); d681 2 a682 2 rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp, index_mark, rel_lookup_flag); d717 3 a719 3 if (config->index_open && *p == config->index_open) { rc = num_exp(++p, end, index_mark, &idx, &failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d732 1 a732 1 if (*p != config->index_close) { d742 1 a742 1 if (p == end || (*p != config->delim_close && *p != ':')) { d756 1 a756 1 rc = (*lookup) (NULL, lookup_context, name.begin, name.end - name.begin, idx, d792 3 a794 3 rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result, index_mark, rel_lookup_flag); d796 3 a798 3 rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp, index_mark, rel_lookup_flag); d806 1 a806 1 if (p == end || *p != config->delim_close) { d830 7 a836 5 static int variable(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int index_mark, int *rel_lookup_flag) d850 1 a850 1 if (p == end || *p != config->delim_init) d859 1 a859 1 rc = varname(p, end, nameclass); d863 1 a863 1 rc2 = (*lookup)(NULL, lookup_context, p, rc, 0, &data, &len, &buffer_size); d880 2 a881 2 rc = expression(p, end, config, nameclass, lookup, lookup_context, force_expand, result, index_mark, rel_lookup_flag); d887 7 a893 6 static int exptext_or_variable(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int index_mark, int *rel_lookup_flag) d906 1 a906 1 rc = exptext(p, end, config); d917 2 a918 2 rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp, index_mark, rel_lookup_flag); d940 7 a946 6 static int substext_or_variable(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int index_mark, int *rel_lookup_flag) d959 1 a959 1 rc = substext(p, end, config); d970 2 a971 2 rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp, index_mark, rel_lookup_flag); d993 1 a993 1 static int expand_class_description(tokenbuf_t *src, tokenbuf_t *dst) d1027 1 a1027 1 if ((rc = expand_class_description(search, &srcclass)) != VAR_OK) d1029 1 a1029 1 if ((rc = expand_class_description(replace, &dstclass)) != VAR_OK) d1115 1 a1115 1 static int expand_regex_replace(const char *data, tokenbuf_t *orig, d1267 1 a1267 1 rc = expand_regex_replace(p, replace, pmatch, &myreplace); d1427 6 a1432 4 static int command(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *data, int index_mark, int *rel_lookup_flag) d1488 1 a1488 1 rc = number(p, end); d1509 1 a1509 1 rc = number(p, end); d1536 3 a1538 3 rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf, index_mark, rel_lookup_flag); d1554 2 a1555 2 rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf, index_mark, rel_lookup_flag); d1577 2 a1578 2 rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf, index_mark, rel_lookup_flag); d1599 2 a1600 2 rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search, index_mark, rel_lookup_flag); d1611 2 a1612 2 rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace, index_mark, rel_lookup_flag); d1623 1 a1623 1 rc = exptext(p, end, config); d1645 2 a1646 2 rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search, index_mark, rel_lookup_flag); d1657 2 a1658 2 rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace, index_mark, rel_lookup_flag); d1684 1 a1684 1 rc = number(p, end); d1700 2 a1701 2 rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace, index_mark, rel_lookup_flag); d1750 7 a1756 10 struct wrapper_context { var_cb_value_t lookup; void *context; int *rel_lookup_flag; }; static int lookup_wrapper(var_t *var, void *context, const char *name, size_t name_len, int idx, const char **data, size_t *data_len, size_t *buffer_size) d1758 1 a1758 23 static char buf[1]; struct wrapper_context *wcon = context; int rc; rc = (*wcon->lookup)(NULL, wcon->context, name, name_len, idx, data, data_len, buffer_size); if (rc == VAR_ERR_UNDEFINED_VARIABLE) { (*wcon->rel_lookup_flag)--; *data = buf; *data_len = 0; *buffer_size = 0; return VAR_OK; } return rc; } static var_rc_t loop_limits(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void* lookup_context, int* start, int* step, int* stop, int* open_end) { const char* p = begin; d1763 1 d1766 1 a1766 2 if (*p != config->delim_open) d1769 1 a1769 1 ++p; a1771 1 d1773 1 a1773 2 rc = num_exp(p, end, 0, start, &failed, &dummy, config, nameclass, lookup, lookup_context); d1786 1 a1786 1 ++p; a1788 1 d1790 1 a1790 2 rc = num_exp(p, end, 0, step, &failed, &dummy, config, nameclass, lookup, lookup_context); d1799 2 a1800 4 if (*p != ',') { if (*p != config->delim_close) d1802 2 a1803 3 else { ++p; a1810 1 } d1812 1 d1814 1 a1814 1 ++p; a1816 1 d1818 3 a1820 5 rc = num_exp(p, end, 0, stop, &failed, &dummy, config, nameclass, lookup, lookup_context); if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) { *stop = 0; /* use default */ d1822 1 a1822 1 } d1825 1 a1825 2 else { d1828 1 a1828 1 } d1831 1 a1831 2 if (*p != config->delim_close) d1835 33 d1870 12 a1881 6 static var_rc_t input(const char *begin, const char *end, const var_syntax_t *config, const char_class_t nameclass, var_cb_value_t lookup, void *lookup_context, int force_expand, tokenbuf_t *output, int index_mark, size_t recursion_level, int *rel_lookup_flag) d1889 1 a1889 1 struct wrapper_context wcon; d1902 1 a1902 1 if (begin != end && config->index_open && *begin == config->index_open) { a1904 3 wcon.lookup = lookup; wcon.context = lookup_context; wcon.rel_lookup_flag = rel_lookup_flag; d1919 15 a1933 2 rc = input(begin, end, config, nameclass, &lookup_wrapper, &wcon, 1, output, i, recursion_level+1, rel_lookup_flag); d1936 1 a1936 1 if (begin[rc] != config->index_close) { d1940 4 a1943 5 if (loop_limit_length < 0) { rc2 = loop_limits(begin + rc + 1, end, config, nameclass, lookup, lookup_context, &start, &step, &stop, &open_end); a1944 1 { a1945 1 } a1946 1 { d1948 1 a1948 3 } else if (rc2 > 0) { a1951 1 } d1953 1 d1965 1 a1965 2 rc = text(begin, end, config->delim_init, config->index_open, config->index_close, config->escape); d1976 3 a1978 3 rc = variable(begin, end, config, nameclass, lookup, lookup_context, force_expand, &result, index_mark, rel_lookup_flag); d2007 5 a2011 1 /* ------------------------------------------------------------------ */ d2147 1 a2147 1 *dst = '\0'; d2158 1 a2158 1 var_expand_t ctx; d2175 3 a2177 4 rc = input(src_ptr, src_ptr + src_len, &var->syntax, var->syntax_nameclass, var->cb_value_fct, var->cb_value_ctx, ctx.force_expand, &output, 0, 0, NULL); @ 1.68 log @merge old internal_expand() into new var_expand() @ text @d715 1 a715 1 rc = (*lookup) (lookup_context, name.begin, name.end - name.begin, idx, d820 1 a820 1 rc2 = (*lookup)(lookup_context, p, rc, 0, &data, &len, &buffer_size); d1709 1 a1709 1 static int lookup_wrapper(void *context, d1718 1 a1718 1 rc = (*wcon->lookup)(wcon->context, name, name_len, @ 1.67 log @shorten table @ text @a1954 45 static var_rc_t internal_expand( const char *input_buf, size_t input_len, char **result, size_t *result_len, var_cb_value_t lookup, void *lookup_context, const var_syntax_t *config, char_class_t nameclass, int force_expand) { var_rc_t rc; tokenbuf_t output; /* Set the result pointer to the begining of the input buffer so that it is correctly initialized in case we fail with an error. */ *result = (char *)input_buf; /* Call the parser. */ tokenbuf_init(&output); rc = input(input_buf, input_buf + input_len, config, nameclass, lookup, lookup_context, force_expand, &output, 0, 0, NULL); /* Post-process output */ if (rc >= 0) { /* always NUL-terminate output for convinience reasons */ if (!tokenbuf_append(&output, "\0", 1)) { tokenbuf_free(&output); return VAR_ERR_OUT_OF_MEMORY; } output.end--; /* Provide results */ *result = (char *)output.begin; if (result_len != NULL) *result_len = output.end - output.begin; /* canonify all positive answers */ rc = VAR_OK; } else { /* Provide error results */ *result = (char *)input_buf; if (result_len != NULL) *result_len = output.end - output.begin; } return VAR_RC(rc); } d2102 2 a2103 1 /* var_expand_t ctx; */ d2110 39 a2148 5 /* ctx.force_expand = force_expand; */ rc = internal_expand(src_ptr, src_len, dst_ptr, dst_len, var->cb_value_fct, var->cb_value_ctx, &var->syntax, var->syntax_nameclass, force_expand); return rc; d2151 1 a2151 1 static char *var_errors[] = { d2205 1 a2205 1 *str = var_errors[rc]; @ 1.66 log @Start internal code and logic cleanups: - move the name class table generation from var_expand() to var_config() by moving the table into var_t. - move inital parts of internal_expand to var_expand(). @ text @d2161 46 a2208 46 static char *errors[] = { "everything ok", /* VAR_OK = 0 */ "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */ "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */ "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */ "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */ "invalid octal value", /* VAR_ERR_INVALID_OCTAL */ "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */ "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */ "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */ "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */ "out of memory", /* VAR_ERR_OUT_OF_MEMORY */ "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */ "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */ "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */ "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */ "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */ "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */ "invalid regular expression in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */ "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */ "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */ "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */ "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */ "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */ "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */ "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */ "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */ "source and destination classes do not match in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */ "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */ "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */ "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */ "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */ "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */ "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */ "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */ "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */ "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */ "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */ "index specification of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */ "index specification of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */ "bracket expression in array variable's index is not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */ "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */ "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */ "invalid character in loop limits" /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */ }; d2212 1 a2212 1 if (rc < 0 || rc >= sizeof(errors) / sizeof(char *)) d2215 1 a2215 1 *str = errors[rc]; @ 1.65 log @typo @ text @d61 2 d66 1 a206 2 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */ d1959 1 a1959 1 const var_syntax_t *config, int force_expand) a1960 1 char_class_t nameclass; a1963 10 /* Argument sanity checks */ if (input_buf == NULL || input_len == 0 || result == NULL || lookup == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT); /* Optionally use default configuration */ if (config == NULL) config = &var_syntax_default; a1967 12 /* Expand the class description for valid variable names. */ if ((rc = expand_character_class(config->name_chars, nameclass)) != VAR_OK) return VAR_RC(rc); /* Make sure that the specials defined in the configuration do not appear in the character name class. */ if (nameclass[(int)config->delim_init] || nameclass[(int)config->delim_open] || nameclass[(int)config->delim_close] || nameclass[(int)config->escape]) return VAR_RC(VAR_ERR_INVALID_CONFIGURATION); d2035 1 d2053 8 a2060 3 if (var->syntax.name_chars != NULL) free(var->syntax.name_chars); var->syntax.name_chars = strdup(s->name_chars); d2143 2 a2144 2 const char *src, size_t srclen, char **dst, size_t *dstlen, d2150 4 d2155 1 a2155 1 rc = internal_expand(src, srclen, dst, dstlen, d2157 1 a2157 1 &var->syntax, force_expand); @ 1.64 log @Try to solve the expansion problem with invalid name characters we encountered in OSSP lmtp2nntp if we expand in two passed with different name character sets. We want that the variable constructs which are not valid in the first pass to be correctly kept (assuming deactivated force_expand!) instead of an error produced. According to Peter Simons, this might be not the correct solution, although THL and RSE are sure it is, because we tried it in detail and it seems to be correct. Nevertheless Peter's comment (in german) for reference: | Ich weiß nicht, ob das unbedingt eine gute Idee ist? Der Fehler | "incomplete variable spec" wird immer dann ausgelöst, wenn innerhalb | der "${...}"-Umgebung ein Zeichen auftritt, das dort nicht sein | dürfte. Einmal kommt das natürlich vor, wenn die gültigen Zeichen für | Variablen von Pass 1 zu Pass 2 verschieden sind ... Aber eine andere | Ursache kann auch sein, daß beispielsweise die schließende "}"-Klammer | fehlt. | | IMHO ist die Tatsache, daß libvar an dieser Stelle einen Fehler | liefert kein Softwarefehler, sondern liegt in der Natur der Sache. Die | "richtige" Lösung für das Problem wäre meiner Meinung nach, in beiden | Pässen die Vereinigung beider Zeichenmengen zuzulassen und dann im | jeweiligen Callback zu testen, ob der Variablenname richtig war oder | nicht. | | Genaugenommen sollte man ohnehin überlegen, ob es nicht günstiger | wäre, mit _einem_ Callback das ganze in einem Pass zu parsen, und | diesen dann einfach zwischen Callback A und B entscheiden zu lassen, | um die tatsächliche Auflösung durchzuführen. @ text @d2191 1 a2191 1 "malformated search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */ @ 1.63 log @URL fixing and additional documents @ text @d656 15 a670 2 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; goto error_return; d741 1 @ 1.62 log @HEADS UPS: Mega-commit, revamping the whole library! - completely new API - completely new manual page @ text @d7 1 a7 1 ** library which can be found at http://www.ossp.org/pkg/var/. @ 1.61 log @style and text cleanups @ text @d34 2 d61 5 a65 11 /* The default configuration for the parser. */ const var_config_t var_config_default = { '$', /* varinit */ '{', /* startdelim */ '}', /* enddelim */ '[', /* startindex */ ']', /* endindex */ '#', /* current_index */ '\\', /* escape */ "a-zA-Z0-9_" /* namechars */ d68 4 a71 1 /* The var_strerror() routine will map a var_rc_t into a text message. */ d73 11 a83 54 const char *var_strerror(var_rc_t rc) { static char *var_errors[] = { "everything ok", /* VAR_OK = 0 */ "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */ "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */ "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */ "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */ "invalid octal value", /* VAR_ERR_INVALID_OCTAL */ "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */ "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */ "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */ "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */ "out of memory", /* VAR_ERR_OUT_OF_MEMORY */ "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */ "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */ "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */ "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */ "malformated search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */ "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */ "invalid regular expression in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */ "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */ "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */ "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */ "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */ "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */ "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */ "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */ "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */ "source and destination classes do not match in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */ "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */ "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */ "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */ "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */ "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */ "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */ "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */ "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */ "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */ "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */ "index specification of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */ "index specification of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */ "bracket expression in array variable's index is not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */ "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */ "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */ "invalid character in loop limits" /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */ }; rc = 0 - rc; if (rc < 0 || rc >= sizeof(var_errors) / sizeof(char *)) return "unknown error"; return var_errors[rc]; } a342 60 var_rc_t var_unescape(const char *src, size_t len, char *dst, int unescape_all) { const char *end = src + len; var_rc_t rc; while (src < end) { if (*src == '\\') { if (++src == end) return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER); switch (*src) { case '\\': if (!unescape_all) { *dst++ = '\\'; } *dst++ = '\\'; break; case 'n': *dst++ = '\n'; break; case 't': *dst++ = '\t'; break; case 'r': *dst++ = '\r'; break; case 'x': ++src; if ((rc = expand_hex(&src, &dst, end)) != VAR_OK) return VAR_RC(rc); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (end - src >= 3 && isdigit((int)src[1]) && isdigit((int)src[2])) { if ((rc = expand_octal(&src, &dst, end)) != 0) return VAR_RC(rc); break; } default: if (!unescape_all) { *dst++ = '\\'; } *dst++ = *src; } ++src; } else *dst++ = *src++; } *dst = '\0'; return VAR_OK; } d346 3 a348 3 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int current_index, d351 4 a354 4 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf_t *data, int current_index, int* rel_lookup_flag); static int num_exp(const char *begin, const char *end, int current_index, d356 1 a356 1 const var_config_t *config, d358 1 a358 1 var_cb_t lookup, void* lookup_context); d360 2 a361 2 static int text(const char *begin, const char *end, char varinit, char startindex, char endindex, char escape) d370 1 a370 1 else if (*p == varinit) d372 1 a372 1 else if (startindex && (*p == startindex || *p == endindex)) d398 1 a398 1 const var_config_t *config) d402 1 a402 1 for (p = begin; p != end && *p != config->varinit && *p != '/'; p++) { d413 1 a413 1 const var_config_t *config) d419 2 a420 2 && *p != config->varinit && *p != config->enddelim d443 1 a443 1 static int num_exp_read_operand(const char *begin, const char *end, int current_index, d445 1 a445 1 const var_config_t *config, d447 1 a447 1 var_cb_t lookup, void *lookup_context) d459 1 a459 1 rc = num_exp(++p, end, current_index, result, failed, d470 1 a470 1 else if (*p == config->varinit) { d472 1 a472 1 lookup_context, 1, &tmp, current_index, rel_lookup_flag); d476 1 a476 1 lookup_context, 0, &tmp, current_index, rel_lookup_flag); d486 1 a486 1 rc = num_exp(tmp.begin, tmp.end, current_index, result, d493 1 a493 1 else if (config->current_index && *p == config->current_index) { d495 1 a495 1 *result = current_index; d524 1 a524 1 static int num_exp(const char *begin, const char *end, int current_index, d526 1 a526 1 const var_config_t *config, d528 1 a528 1 var_cb_t lookup, void *lookup_context) d538 1 a538 1 rc = num_exp_read_operand(p, end, current_index, result, d548 1 a548 1 rc = num_exp(p, end, current_index, &right, failed, d560 1 a560 1 rc = num_exp_read_operand(p, end, current_index, &right, failed, d596 2 a597 2 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, d599 1 a599 1 tokenbuf_t *result, int current_index, int *rel_lookup_flag) d618 1 a618 1 if (p == end || *p != config->startdelim) d640 1 a640 1 force_expand, &tmp, current_index, rel_lookup_flag); d662 2 a663 2 if (config->startindex && *p == config->startindex) { rc = num_exp(++p, end, current_index, &idx, &failed, d677 1 a677 1 if (*p != config->endindex) { d687 1 a687 1 if (p == end || (*p != config->enddelim && *p != ':')) { d738 1 a738 1 current_index, rel_lookup_flag); d742 1 a742 1 current_index, rel_lookup_flag); d750 1 a750 1 if (p == end || *p != config->enddelim) { d775 3 a777 3 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf_t *result, int current_index, d792 1 a792 1 if (p == end || *p != config->varinit) d823 1 a823 1 force_expand, result, current_index, rel_lookup_flag); d830 2 a831 2 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, d833 1 a833 1 tokenbuf_t *result, int current_index, d859 1 a859 1 force_expand, &tmp, current_index, rel_lookup_flag); d882 2 a883 2 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, d885 1 a885 1 tokenbuf_t *result, int current_index, d911 1 a911 1 force_expand, &tmp, current_index, rel_lookup_flag); d1368 3 a1370 3 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf_t *data, int current_index, int *rel_lookup_flag) d1476 1 a1476 1 current_index, rel_lookup_flag); d1493 1 a1493 1 lookup_context, force_expand, &tmptokbuf, current_index, rel_lookup_flag); d1516 1 a1516 1 lookup_context, force_expand, &tmptokbuf, current_index, rel_lookup_flag); d1538 1 a1538 1 lookup_context, force_expand, &search, current_index, rel_lookup_flag); d1550 1 a1550 1 lookup_context, force_expand, &replace, current_index, rel_lookup_flag); d1584 1 a1584 1 lookup_context, force_expand, &search, current_index, rel_lookup_flag); d1596 1 a1596 1 lookup_context, force_expand, &replace, current_index, rel_lookup_flag); d1639 1 a1639 1 lookup_context, force_expand, &replace, current_index, rel_lookup_flag); d1689 1 a1689 1 var_cb_t lookup; d1716 1 a1716 1 const var_config_t *config, d1718 1 a1718 1 var_cb_t lookup, void* lookup_context, d1729 1 a1729 1 if (*p != config->startdelim) d1769 1 a1769 1 if (*p != config->enddelim) d1806 1 a1806 1 if (*p != config->enddelim) d1813 2 a1814 2 const var_config_t *config, const char_class_t nameclass, var_cb_t lookup, d1816 1 a1816 1 tokenbuf_t *output, int current_index, d1838 1 a1838 1 if (begin != end && config->startindex && *begin == config->startindex) { d1862 1 a1862 1 if (begin[rc] != config->endindex) { d1897 2 a1898 2 rc = text(begin, end, config->varinit, config->startindex, config->endindex, config->escape); d1911 1 a1911 1 current_index, rel_lookup_flag); d1940 5 a1944 4 var_rc_t var_expand(const char *input_buf, size_t input_len, char **result, size_t *result_len, var_cb_t lookup, void *lookup_context, const var_config_t *config, int force_expand) d1958 1 a1958 1 config = &var_config_default; d1965 1 a1965 1 if ((rc = expand_character_class(config->namechars, nameclass)) != VAR_OK) d1970 3 a1972 3 if (nameclass[(int)config->varinit] || nameclass[(int)config->startdelim] || nameclass[(int)config->enddelim] || d2006 209 @ 1.60 log @add optional OSSP ex based exception handling support @ text @d2 1 a2 1 ** VAR - OSSP variable expression library. d6 1 a6 1 ** This file is part of OSSP VAR, an extensible data serialization d27 1 a27 1 ** var.c: VAR library implementation. d77 43 a119 44 "OK", /* VAR_OK = 0 */ "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER = -1 */ "incomplete hex", /* VAR_ERR_INCOMPLETE_HEX = -2 */ "invalid hex", /* VAR_ERR_INVALID_HEX = -3 */ "octal too large", /* VAR_ERR_OCTAL_TOO_LARGE = -4 */ "invalid octal", /* VAR_ERR_INVALID_OCTAL = -5 */ "incomplete octal", /* VAR_ERR_INCOMPLETE_OCTAL = -6 */ "incomplete grouped hex", /* VAR_ERR_INCOMPLETE_GROUPED_HEX = -7 */ "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC = -8 */ "var_expand() configuration is inconsistent", /* VAR_ERR_INVALID_CONFIGURATION = -9 */ "out of memory", /* VAR_ERR_OUT_OF_MEMORY = -10 */ "incomplete variable", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC = -11 */ "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE = -12 */ "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE = -13 */ "unknown command in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR = -14 */ "unknown error", /* -15 is not used */ "malformated search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE = -16 */ "unknown flag specified in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG = -17 */ "invalid regular expression in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE = -18 */ "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND = -19 */ "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING = -20 */ "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET = -21 */ "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER = -22 */ "range in cut operation is out of bounds", /* VAR_ERR_RANGE_OUT_OF_BOUNDS = -23 */ "offset in cut operation is out of bounds", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS = -24 */ "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC = -25 */ "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE = -26 */ "source class does not match destination class in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH = -27 */ "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS = -28 */ "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC = -29 */ "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING = -30 */ "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH = -31 */ "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING = -32 */ "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE = -33 */ "submatch referred to in replace string does not exist in search string", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE = -34 */ "invalid argument", /* VAR_ERR_INVALID_ARGUMENT = -35 */ "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR = -36 */ "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED = -37 */ "index specification of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC = -38 */ "index specification of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC = -39 */ "bracket expression in array variable's index is not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX = -40 */ "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX = -41 */ "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT = -42 */ "invalid character in loop limits" /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS = -43 */ @ 1.59 log @- Compile regular expressions with REG_NEWLINE flag so that '^' and '$' match in the middle of the string where appropriate. - Fixed incorrect results of search_and_replace() with zero-length matches, such as '^'. @ text @d30 4 d46 13 d74 1 a74 1 const char* var_strerror(var_rc_t rc) d397 1 a397 1 return VAR_ERR_INCOMPLETE_NAMED_CHARACTER; d417 1 a417 1 return rc; d431 1 a431 1 return rc; d2058 1 a2058 1 return VAR_ERR_INVALID_ARGUMENT; d2070 1 a2070 1 return rc; d2078 1 a2078 1 return VAR_ERR_INVALID_CONFIGURATION; d2109 1 a2109 1 return rc; d2111 1 @ 1.58 log @bump copyright year @ text @d1275 1 a1275 1 rc = regcomp(&preg, tmp.begin, REG_EXTENDED|((case_insensitive)?REG_ICASE:0)); d1290 2 a1291 3 if (regexec (&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH) { d1311 13 a1323 1 p += (pmatch[0].rm_eo > 0) ? pmatch[0].rm_eo : 1; d1327 6 a1332 1 tokenbuf_append(&tmp, p, mydata.end - p); @ 1.57 log @allow result_len to be NULL @ text @d3 2 a4 2 ** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) @ 1.56 log @Finally adjust return code semantics of var_cb_t to use var_rc_t only. @ text @d2023 1 a2023 1 result == NULL || result_len == NULL || d2063 2 a2064 1 *result_len = output.end - output.begin; d2072 2 a2073 1 *result_len = output.end - output.begin; /* FIXME */ @ 1.55 log @silence gcc @ text @d791 1 a791 3 if (rc < 0) goto error_return; if (rc == 0) { d794 1 a794 2 if (force_expand) { rc = VAR_ERR_UNDEFINED_VARIABLE; a795 1 } d804 3 d894 1 a894 5 if (rc2 < 0) return rc2; if (rc2 == 0) { if (force_expand) return VAR_ERR_UNDEFINED_VARIABLE; d900 2 d1777 1 a1777 1 if (rc == 0) { d1782 1 a1782 1 return 1; d1784 1 a1784 2 else return rc; @ 1.54 log @remove debugging messages @ text @d1925 2 @ 1.53 log @Implemented loops with explicit start, stop, and step values. @ text @a1801 2 printf("loop_limits() called: '%s'.\n", begin); a1946 1 printf("loop_limits() failed: %d ('%s').\n", rc2, var_strerror(rc2)); a1954 2 printf("Found loop limits: start = %d, step = %d, stop = %d, open_end = %s\n", start, step, stop, (open_end) ? "true" : "false"); @ 1.52 log @remove debugging stuff @ text @d102 2 a103 1 "unterterminated loop construct" /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT = -42 */ d506 2 a507 2 p != end && *p != config->varinit d547 1 a547 1 rc = num_exp(++p, end, current_index, result, failed, d559 1 a559 1 rc = variable(p, end, config, nameclass, lookup, d563 1 a563 1 rc = variable(p, end, config, nameclass, lookup, d574 1 a574 1 rc = num_exp(tmp.begin, tmp.end, current_index, result, d626 2 a627 2 rc = num_exp_read_operand(p, end, current_index, result, failed, rel_lookup_flag, config, nameclass, d636 1 a636 1 rc = num_exp(p, end, current_index, &right, failed, d648 1 a648 1 rc = num_exp_read_operand(p, end, current_index, &right, failed, d751 1 a751 1 rc = num_exp(++p, end, current_index, &idx, &failed, d826 1 a826 1 lookup_context, force_expand, result, d830 1 a830 1 lookup_context, force_expand, &tmp, d1235 1 a1235 1 if (rc != 0) { d1356 1 a1356 1 i = (width - (data->end - data->begin)) d1373 1 a1373 1 i = (width - (data->end - data->begin)) d1400 1 a1400 1 i = ((width - (data->end - data->begin)) / 2) d1778 1 a1778 1 rc = (*wcon->lookup)(wcon->context, name, name_len, d1791 99 d1898 1 a1898 1 int rc; d1900 1 d1906 1 d1918 1 d1923 9 a1931 1 for (i = 0; i == 0 || *rel_lookup_flag > original_rel_lookup_state; i++) { d1934 1 a1934 1 rc = input(begin, end, config, nameclass, &lookup_wrapper, d1942 23 d1966 4 a1969 1 output->end = output->begin + output_backup; d1972 1 d1976 1 a1976 1 rc = text(begin, end, config->varinit, config->startindex, d1989 1 a1989 1 lookup_context, force_expand, &result, a2082 1 @ 1.51 log @Cleanup post-processing code by reducing redundancy. Nevertheless the error result_len is still incorrect IMHO! @ text @a1822 1 printf("%d: Recursing with default index %d.\n", recursion_level, i); a1824 2 printf("%d: input() recursion returned %d.\n", recursion_level, rc); printf("%d: input(): Had %d relative lookups.\n", recursion_level, *rel_lookup_flag); @ 1.50 log @Get rid of the following warnings under "gcc -O2 -pedantic -Wall -Wshadow -Wpointer-arith -Wcast-align -Winline -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Wno-long-long": var.h:91: warning: declaration of `index' shadows global declaration var.c: In function `expression': var.c:693: warning: declaration of `index' shadows global declaration var.c: At top level: var.c:1769: warning: declaration of `varname' shadows global declaration var.c:1769: warning: declaration of `index' shadows global declaration var.c: In function `lookup_wrapper': var.c:1772: warning: declaration of `index' shadows global declaration var.c:1772: warning: declaration of `varname' shadows global declaration var_test.c:13: warning: declaration of `index' shadows global declaration var_test.c: In function `var_lookup': var_test.c:16: warning: declaration of `index' shadows global declaration @ text @d1923 2 a1924 2 *result = (char *)output.begin; *result_len = output.end - output.begin; d1926 1 d1932 6 d1940 6 a1945 2 *result = (char *)output.begin; *result_len = output.end - output.begin; @ 1.49 log @Manual OSSP coding style adjustments. @ text @d693 1 a693 1 int index = 0; d750 1 a750 1 rc = num_exp(++p, end, current_index, &index, &failed, d788 1 a788 1 rc = (*lookup) (lookup_context, name.begin, name.end - name.begin, index, d1769 1 a1769 1 const char *varname, size_t name_len, int index, d1777 2 a1778 2 rc = (*wcon->lookup)(wcon->context, varname, name_len, index, data, data_len, buffer_size); @ 1.48 log @Fixed syntax error. @ text @d449 1 a449 1 { d451 3 a453 4 for (p = begin; p != end; ++p) { if (*p == escape) { d456 1 a456 1 } a457 1 { a458 1 } a459 1 { d461 1 a461 2 } } d463 1 a463 1 } d469 3 a471 1 for (p = begin; p != end && nameclass[(int) *p]; ++p); d478 3 a480 1 for (p = begin; p != end && isdigit((int)*p); ++p); d488 2 a489 1 for (p = begin; p != end && *p != config->varinit && *p != '/'; ++p) { d493 1 a493 1 ++p; d503 1 d505 4 a508 2 p != end && *p != config->varinit && *p != config->enddelim && *p != ':'; ++p) { d512 1 a512 1 ++p; d518 2 a519 2 static int num_exp_read_int(const char** begin, const char* end) { d521 2 a522 2 do { d526 1 a526 2 } while (isdigit(**begin) && *begin != end); d528 1 a528 1 } d531 1 a531 1 int* result, int* failed, int* rel_lookup_flag, d534 2 a535 2 var_cb_t lookup, void* lookup_context) { d545 3 a547 3 if (*p == '(') { rc = num_exp(++p, end, current_index, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d556 5 a560 6 } else if (*p == config->varinit) { rc = variable(p, end, config, nameclass, lookup, lookup_context, 1, &tmp, current_index, rel_lookup_flag); if (rc == VAR_ERR_UNDEFINED_VARIABLE) { d562 2 a563 1 rc = variable(p, end, config, nameclass, lookup, lookup_context, 0, &tmp, current_index, rel_lookup_flag); d568 2 a569 3 } else { d573 2 a574 1 rc = num_exp(tmp.begin, tmp.end, current_index, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); a577 1 } d579 3 a581 3 else if (config->current_index && *p == config->current_index) { ++p; d583 3 a585 4 ++(*rel_lookup_flag); } else if (isdigit(*p)) { d587 5 a592 7 else if (*p == '+') { if (end - p > 1 && isdigit(p[1])) { ++p; *result = num_exp_read_int(&p, end); } d595 4 a598 6 } else if (*p == '-') { if (end - p > 1 && isdigit(p[1])) { ++p; d601 1 a601 1 } d604 1 a604 1 } d609 1 a609 1 } d612 1 a612 1 int* result, int* failed, int* rel_lookup_flag, d615 3 a617 3 var_cb_t lookup, void* lookup_context) { const char* p = begin; d625 3 a627 1 rc = num_exp_read_operand(p, end, current_index, result, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d632 2 a633 4 while (p != end) { if (*p == '+' || *p == '-') { d635 2 a636 1 rc = num_exp(p, end, current_index, &right, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d644 2 a645 3 } else if (*p == '*' || *p == '/' || *p == '%') { d647 2 a648 1 rc = num_exp_read_operand(p, end, current_index, &right, failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d652 1 a652 2 if (operator == '*') { d654 3 a656 5 } else if (operator == '/') { if (right == 0) { d661 1 a661 1 } d664 3 a666 5 } else if (operator == '%') { if (right == 0) { d671 1 a671 1 } a673 1 } d675 1 d678 1 a678 2 } d680 1 a680 1 } d686 1 a686 1 tokenbuf_t *result, int current_index, int* rel_lookup_flag) d714 1 a714 2 do { d718 2 a719 4 if (rc > 0) { if (!tokenbuf_append(&name, p, rc)) { d722 1 a722 1 } d724 1 a724 1 } d730 2 a731 4 if (rc > 0) { if (!tokenbuf_append(&name, tmp.begin, tmp.end - tmp.begin)) { d734 1 a734 1 } a735 1 } d737 1 a737 1 while (rc > 0); d742 1 a742 2 if (name.begin == name.end) { d745 1 a745 1 } d749 3 a751 3 if (config->startindex && *p == config->startindex) { rc = num_exp(++p, end, current_index, &index, &failed, rel_lookup_flag, config, nameclass, lookup, lookup_context); d754 1 a754 2 if (rc == 0) { d757 1 a757 1 } d760 1 a760 2 if (p == end) { d763 2 a764 3 } if (*p != config->endindex) { a766 2 } ++p; d768 2 d774 1 a774 2 if (p == end || (*p != config->enddelim && *p != ':')) { d777 2 a778 2 } ++p; d782 1 a782 2 if (failed) { d786 2 a787 3 } else { d792 1 a792 2 if (rc == 0) { d795 1 a795 3 if (force_expand) { d798 2 a799 1 } a801 1 d806 2 a807 3 } else { a809 1 a812 1 } d814 1 d816 1 a816 2 if (p[-1] == ':') { d820 3 a822 4 --p; while (p != end && *p == ':') { ++p; d825 2 a826 1 lookup_context, force_expand, result, current_index, rel_lookup_flag); d829 2 a830 1 lookup_context, force_expand, &tmp, current_index, rel_lookup_flag); d836 1 a836 1 } d838 1 a838 2 if (p == end || *p != config->enddelim) { d841 2 a842 2 } ++p; d844 2 a845 2 ++result->end; } d860 1 a860 1 } d866 1 a866 1 int* rel_lookup_flag) d893 1 a893 1 rc2 = (*lookup) (lookup_context, p, rc, 0, &data, &len, &buffer_size); d915 1 a915 1 ++rc; d924 1 a924 1 int* rel_lookup_flag) d960 1 a960 2 } while (rc > 0); d976 1 a976 1 int* rel_lookup_flag) d1012 1 a1012 2 } while (rc > 0); a1022 1 d1027 1 d1033 1 a1033 1 if (!tokenbuf_append(dst, (char *) &c, 1)) d1040 1 a1040 1 ++p; d1073 1 a1073 2 if (!tokenbuf_assign (&tmp, data->begin, data->end - data->begin)) { d1106 5 a1110 2 size_t num1 = tokenbuf_toint(number1); size_t num2 = tokenbuf_toint(number2); d1124 2 a1125 1 } else { /* OK, then use num2. */ d1159 1 a1159 1 ++p; d1165 1 a1165 1 ++p; d1173 1 a1173 1 ++p; d1188 1 a1188 1 ++p; d1234 2 a1235 1 if (rc != 0) { /* no match, copy character */ d1243 1 a1243 1 replace->end - replace->begin); d1268 1 a1268 2 if (!tokenbuf_assign (&tmp, search->begin, search->end - search->begin)) d1270 1 a1270 2 if (!tokenbuf_assign (&mydata, data->begin, data->end - data->begin)) { d1277 1 a1277 2 rc = regcomp(&preg, tmp.begin, REG_EXTENDED | ((case_insensitive) ? REG_ICASE : 0)); d1287 1 a1287 1 for (p = mydata.begin; p != mydata.end;) { d1353 1 a1353 1 --i; d1355 2 a1356 2 i = (width - (data->end - data->begin)) % (fill->end - fill->begin); d1370 1 a1370 1 --i; d1372 2 a1373 2 i = (width - (data->end - data->begin)) % (fill->end - fill->begin); d1378 1 a1378 2 if (!tokenbuf_append (&result, data->begin, data->end - data->begin)) { d1393 1 a1393 2 if (!tokenbuf_append (&result, fill->begin, fill->end - fill->begin)) { d1397 1 a1397 1 --i; d1399 2 a1400 2 i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin); d1408 1 a1408 2 if (!tokenbuf_append (&result, data->begin, data->end - data->begin)) { d1423 1 a1423 1 --i; d1444 1 a1444 1 tokenbuf_t *data, int current_index, int* rel_lookup_flag) d1471 1 a1471 2 if (!tokenbuf_assign (data, data->begin, data->end - data->begin)) { d1476 1 a1476 1 for (ptr = (char *) data->begin; ptr != data->end; ++ptr) d1479 1 a1479 1 ++p; d1547 1 a1547 1 ++p; d1565 1 a1565 1 ++p; d1588 1 a1588 1 ++p; d1605 1 a1605 1 ++p; d1609 1 a1609 1 ++p; d1621 1 a1621 1 ++p; d1633 1 a1633 1 ++p; d1651 1 a1651 1 ++p; d1655 1 a1655 1 ++p; d1667 1 a1667 1 ++p; d1690 1 a1690 1 ++p; d1694 1 a1694 1 ++p; d1710 1 a1710 1 ++p; d1722 1 a1722 1 ++p; d1728 1 a1728 1 ++p; d1762 1 a1762 2 struct wrapper_context { d1764 3 a1766 3 void* context; int* rel_lookup_flag; }; d1772 1 a1772 1 { d1774 1 a1774 1 struct wrapper_context* wcon = context; d1777 4 a1780 4 rc = (*wcon->lookup)(wcon->context, varname, name_len, index, data, data_len, buffer_size); if (rc == 0) { --(*wcon->rel_lookup_flag); d1785 1 a1785 1 } d1788 1 a1788 1 } d1795 1 a1795 1 size_t recursion_level, int* rel_lookup_flag) d1797 1 a1797 1 const char* p = begin; d1805 1 d1808 1 a1808 2 if (rel_lookup_flag == NULL) { d1811 1 a1811 1 } d1814 1 a1814 2 if (begin != end && config->startindex && *begin == config->startindex) { d1819 2 a1820 3 ++begin; for (i = 0; i == 0 || *rel_lookup_flag > original_rel_lookup_state; ++i) { d1824 2 a1825 1 rc = input(begin, end, config, nameclass, &lookup_wrapper, &wcon, 1, output, i, recursion_level+1, rel_lookup_flag); d1830 1 a1830 2 if (begin[rc] != config->endindex) { a1832 1 } d1834 1 d1837 1 a1837 1 ++begin; d1839 1 a1839 1 } d1841 2 a1842 1 rc = text(begin, end, config->varinit, config->startindex, config->endindex, config->escape); d1854 2 a1855 1 lookup_context, force_expand, &result, current_index, rel_lookup_flag); d1857 1 a1857 2 if (!tokenbuf_append (output, result.begin, result.end - result.begin)) { d1866 1 a1866 2 } while (begin != end && rc > 0); d1925 2 a1926 4 if (rc >= 0) { if (!tokenbuf_append(&output, "\0", 1)) { d1929 2 a1930 2 } --output.end; d1932 1 a1932 2 } d1937 1 @ 1.47 log @Implemented correct termination and recursion for loop constructs. @ text @a1836 1 tokenbuf_init(&result); d1839 1 @ 1.46 log @Variables that have no index specified are always looked up with index zero now; the default index does no longer apply. @ text @d432 9 a440 4 static int variable(const char *, const char *, const var_config_t *, const char_class_t, var_cb_t, void *, int, tokenbuf_t *, int); static int command(const char *, const char *, const var_config_t *, const char_class_t, var_cb_t, void *, int, tokenbuf_t *, int); d442 2 a443 1 int* result, int* failed, const var_config_t *config, d529 2 a530 1 int* result, int* failed, const var_config_t *config, a542 2 printf("Parsing for operand: '%s'\n", p); d545 1 a545 1 rc = num_exp(++p, end, current_index, result, failed, config, nameclass, lookup, lookup_context); d557 1 a557 1 rc = variable(p, end, config, nameclass, lookup, lookup_context, 1, &tmp, current_index); d561 1 a561 1 rc = variable(p, end, config, nameclass, lookup, lookup_context, 0, &tmp, current_index); a564 1 printf("Expanding variable() failed, eating %d characters and substituting 0.\n", rc); d572 1 a572 1 rc = num_exp(tmp.begin, tmp.end, current_index, result, failed, config, nameclass, lookup, lookup_context); d582 1 d616 2 a617 1 int* result, int* failed, const var_config_t *config, d629 1 a629 1 rc = num_exp_read_operand(p, end, current_index, result, failed, config, nameclass, lookup, lookup_context); a632 1 printf("left operand is %d\n", *result); a635 1 printf("Parsing for operator: '%s'\n", p); d639 1 a639 2 printf("Operator is '%c'\n", operator); rc = num_exp(p, end, current_index, &right, failed, config, nameclass, lookup, lookup_context); a642 1 printf("Calculating %d %c %d --> ", *result, operator, right); a646 1 printf("%d\n", *result); d651 1 a651 4 printf("Operator is '%c'\n", operator); rc = num_exp_read_operand(p, end, current_index, &right, failed, config, nameclass, lookup, lookup_context); a654 1 printf("Calculating %d %c %d --> ", *result, operator, right); a682 1 printf("%d\n", *result); a685 2 printf("Parsing for right: '%s'\n", p); d695 1 a695 1 tokenbuf_t *result, int current_index) d739 1 a739 1 force_expand, &tmp, current_index); d767 1 a767 2 printf("Found START-INDEX: %s\n", p); rc = num_exp(++p, end, current_index, &index, &failed, config, nameclass, lookup, lookup_context); a776 2 printf("Expecting END-INDEX: %s\n", p); a787 2 printf("Found index %d.\n", index); d854 1 a854 1 lookup_context, force_expand, result, current_index); d857 1 a857 1 lookup_context, force_expand, &tmp, current_index); d893 2 a894 1 int force_expand, tokenbuf_t *result, int current_index) d941 1 a941 1 force_expand, result, current_index); d951 2 a952 1 tokenbuf_t *result, int current_index) d977 1 a977 1 force_expand, &tmp, current_index); d1004 2 a1005 1 tokenbuf_t *result, int current_index) d1030 1 a1030 1 force_expand, &tmp, current_index); d1476 1 a1476 1 tokenbuf_t *data, int current_index) d1582 2 a1583 1 lookup_context, force_expand, &tmptokbuf, current_index); d1600 1 a1600 1 lookup_context, force_expand, &tmptokbuf, current_index); d1623 1 a1623 1 lookup_context, force_expand, &tmptokbuf, current_index); d1645 1 a1645 1 lookup_context, force_expand, &search, current_index); d1657 1 a1657 1 lookup_context, force_expand, &replace, current_index); d1691 1 a1691 1 lookup_context, force_expand, &search, current_index); d1703 1 a1703 1 lookup_context, force_expand, &replace, current_index); d1746 1 a1746 1 lookup_context, force_expand, &replace, current_index); d1799 1 a1799 1 int* found_variables; a1810 4 printf("Looking up '"); fwrite(varname, name_len, 1, stdout); printf("[%d]' ... rc = ", index); a1811 1 printf("%d\n", rc); d1814 1 a1820 2 { *(wcon->found_variables) += 1; a1821 1 } d1828 2 a1829 1 tokenbuf_t *output, int current_index, size_t recursion_level) a1833 1 int found_variables; d1838 8 a1847 2 printf("input(): Parsing string '%s'.\n", begin); d1850 1 a1850 1 printf("Found loop construct.\n"); d1853 1 a1853 1 wcon.found_variables = &found_variables; d1855 1 a1855 1 for (i = 0; i == 0 || found_variables; ++i) d1857 1 d1859 4 a1862 4 found_variables = 0; printf("Recursing with default index %d.\n", i); rc = input(begin, end, config, nameclass, &lookup_wrapper, &wcon, 1, output, i, recursion_level+1); printf("input() recursion returned %d.\n", rc); d1889 1 a1889 1 lookup_context, force_expand, &result, current_index); d1958 1 a1958 1 lookup, lookup_context, force_expand, &output, 0, 0); @ 1.45 log @Guarantee that the buffer returned by var_expand() is terminated with a null byte. @ text @d708 1 a708 1 int index = current_index; @ 1.44 log @Support the loop construct only if startindex has been set. @ text @d1968 14 a1981 1 return (rc >= 0) ? VAR_OK : rc; @ 1.43 log @Implemented the looping construct. However, there are still some semantical issues that need to be clarified before the code is stable. @ text @d1856 1 a1856 1 if (begin != end && *begin == config->startindex) @ 1.42 log @Implemented division-by-zero error. @ text @d101 2 a102 1 "division by zero error in index specification" /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX = -41 */ d433 1 a433 1 const char_class_t, var_cb_t, void *, int, tokenbuf_t *); d435 1 a435 1 const char_class_t, var_cb_t, void *, int, tokenbuf_t *); d442 2 a443 2 char escape) { d445 5 a449 3 for (p = begin; p != end && *p != varinit; ++p) { if (*p == escape) { if (p + 1 == end) d451 9 a459 2 else ++p; d461 1 a462 2 return p - begin; } d552 1 a552 1 rc = variable(p, end, config, nameclass, lookup, lookup_context, 1, &tmp); d556 1 a556 1 rc = variable(p, end, config, nameclass, lookup, lookup_context, 0, &tmp); d701 1 a701 1 tokenbuf_t *result) d708 1 a708 1 int index = 0; d745 1 a745 1 force_expand, &tmp); d774 1 a774 1 rc = num_exp(++p, end, 0, &index, &failed, config, nameclass, lookup, lookup_context); d865 1 a865 1 lookup_context, force_expand, result); d868 1 a868 1 lookup_context, force_expand, &tmp); d904 1 a904 1 int force_expand, tokenbuf_t *result) d951 1 a951 1 force_expand, result); d961 1 a961 1 tokenbuf_t *result) d986 1 a986 1 force_expand, &tmp); d1013 1 a1013 1 tokenbuf_t *result) d1038 1 a1038 1 force_expand, &tmp); d1484 1 a1484 1 tokenbuf_t *data) d1590 1 a1590 1 lookup_context, force_expand, &tmptokbuf); d1607 1 a1607 1 lookup_context, force_expand, &tmptokbuf); d1630 1 a1630 1 lookup_context, force_expand, &tmptokbuf); d1652 1 a1652 1 lookup_context, force_expand, &search); d1664 1 a1664 1 lookup_context, force_expand, &replace); d1698 1 a1698 1 lookup_context, force_expand, &search); d1710 1 a1710 1 lookup_context, force_expand, &replace); d1753 1 a1753 1 lookup_context, force_expand, &replace); d1802 36 d1842 1 a1842 1 tokenbuf_t *output) d1847 4 a1850 1 d1854 31 a1884 1 rc = text(begin, end, config->varinit, config->escape); d1891 1 d1896 1 a1896 1 lookup_context, force_expand, &result); d1904 1 d1909 1 a1909 1 while (rc > 0); d1911 1 a1911 1 if (begin != end) { d1916 1 a1916 1 return VAR_OK; d1965 1 a1965 1 lookup, lookup_context, force_expand, &output); d1968 1 a1968 1 return rc; @ 1.41 log @Implemented force_expand mode in num_exp(). @ text @d100 2 a101 1 "bracket expression in array variable's index is not closed" /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX = -40 */ d551 2 a552 2 printf("Expanding variable() failed, eating %d characters and substituting 1.\n", rc); *result = 1; d650 1 d652 1 d654 11 a664 1 *result = *result / right; d666 11 a676 1 *result = *result % right; @ 1.40 log @Added support for variables as operands in index specifications. @ text @d435 1 a435 1 int* result, const var_config_t *config, d513 1 a513 1 int* result, const var_config_t *config, d530 1 a530 1 rc = num_exp(++p, end, current_index, result, config, nameclass, lookup, lookup_context); d543 20 a562 8 if (rc < 0) return rc; p += rc; rc = num_exp(tmp.begin, tmp.end, current_index, result, config, nameclass, lookup, lookup_context); tokenbuf_free(&tmp); if (rc < 0) return rc; d601 1 a601 1 int* result, const var_config_t *config, d613 1 a613 1 rc = num_exp_read_operand(p, end, current_index, result, config, nameclass, lookup, lookup_context); d626 1 a626 1 rc = num_exp(p, end, current_index, &right, config, nameclass, lookup, lookup_context); d643 1 a643 1 rc = num_exp_read_operand(p, end, current_index, &right, config, nameclass, lookup, lookup_context); d742 1 a742 3 ++p; rc = num_exp(p, end, 0, &index, config, nameclass, lookup, lookup_context); a766 1 d781 1 a781 5 rc = (*lookup) (lookup_context, name.begin, name.end - name.begin, index, &data, &len, &buffer_size); if (rc < 0) goto error_return; if (rc == 0) a782 11 /* The variable is undefined. What we'll do now depends on the force_expand flag. */ if (force_expand) { rc = VAR_ERR_UNDEFINED_VARIABLE; goto error_return; } /* Initialize result to point back to the original text in the buffer. */ a785 1 failed = 1; d789 16 a804 2 /* The preliminary result is the contents of the variable. This may be modified by the commands that may follow. */ d806 14 a819 3 result->begin = data; result->end = data + len; result->buffer_size = buffer_size; @ 1.39 log @We support the current_index and startindex tokens only when they're not zero. @ text @d435 3 a437 1 int* result, const var_config_t *config); d513 3 a515 1 int* result, const var_config_t *config) d518 1 d521 2 d530 1 a530 1 rc = num_exp(++p, end, current_index, result, config); d540 12 d589 3 a591 1 int* result, const var_config_t *config) d601 1 a601 1 rc = num_exp_read_operand(p, end, current_index, result, config); d614 1 a614 1 rc = num_exp(p, end, current_index, &right, config); d631 1 a631 1 rc = num_exp_read_operand(p, end, current_index, &right, config); d732 1 a732 1 rc = num_exp(p, end, 0, &index, config); @ 1.38 log @Added support for indexed variables to expression(). @ text @d533 1 a533 1 else if (*p == config->current_index) d706 1 a706 1 if (*p == config->startindex) @ 1.37 log @Extended the interface of var_cb_t to support variable arrays. To customize your already existing lookup functions quickly to the new interface, just add the "int index" parameter to the function's prototype and add the lines if (index != 0) return VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED; to the function itself. @ text @d75 1 d81 1 a81 1 "start offset missing in cut operation" /* VAR_ERR_MISSING_START_OFFSET = -21 */ d96 5 a100 2 "incomplete quoted pair" /* VAR_ERR_INCOMPLETE_QUOTED_PAIR = -36 */ "lookup function does not support variable arrays" /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED = -37 */ d434 2 d497 134 d637 1 a637 1 { d643 1 d664 2 a665 1 do { d669 4 a672 2 if (rc > 0) { if (!tokenbuf_append(&name, p, rc)) { d675 2 a677 2 p += rc; } d683 4 a686 2 if (rc > 0) { if (!tokenbuf_append(&name, tmp.begin, tmp.end - tmp.begin)) { d689 2 a691 1 p += rc; a692 1 } d698 2 a699 1 if (name.begin == name.end) { d702 36 a737 1 } d739 2 a740 2 /* Now we have the name of the variable stored in "name". We expect an ENDDELIM here. */ d742 2 a743 1 if (p == end || (*p != config->enddelim && *p != ':')) { d746 1 a746 1 } d751 1 a751 1 rc = (*lookup) (lookup_context, name.begin, name.end - name.begin, 0, d755 2 a756 1 if (rc == 0) { d760 2 a761 1 if (force_expand) { d764 1 a764 1 } d772 3 a774 1 } else { d781 1 a781 1 } d783 2 a784 1 if (p[-1] == ':') { d789 2 a790 1 while (p != end && *p == ':') { d803 1 a803 1 } d805 2 a806 1 if (p == end || *p != config->enddelim) { d809 1 a809 1 } d813 1 a813 1 } d828 1 a828 1 } d1505 3 a1507 3 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */ sprintf(buf, "%d", (int)(data->end - data->begin)); tokenbuf_free(data); @ 1.36 log @Changed semantics of the :o operation so that the specified end-offset is included in the result. Updated documentation and test cases appropriately. @ text @d48 3 d96 1 d568 1 a568 1 rc = (*lookup) (lookup_context, name.begin, name.end - name.begin, d670 1 a670 1 rc2 = (*lookup) (lookup_context, p, rc, &data, &len, &buffer_size); @ 1.35 log @Rather than to hard-code the size of the buffer required for format an integer into a string in the ':#' operation, use approximation based on 'sizeof(int)'. @ text @a895 1 d906 1 a906 3 if (!tokenbuf_assign(&res, p, num2 - num1)) //FIXME THL: I expect start-end *inclusive* //FIXME THL: I expect start,len len characters not len-1 @ 1.34 log @THLs review @ text @d1314 3 a1316 3 char buf[32]; //FIXME: thl 2^64 would fit into 20chars ... sprintf(buf, "%d", data->end - data->begin); tokenbuf_free(data); @ 1.33 log @THLs review @ text @d1472 1 a1472 2 else ++p; a1477 5 } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; d1479 4 d1487 2 a1488 2 } else ++p; d1494 1 a1494 2 else p += rc; d1499 2 a1500 2 } else ++p; d1505 2 a1506 2 } else ++p; @ 1.32 log @THLs review @ text @d813 1 a813 2 else ++p; d1433 1 a1433 2 else ++p; d1439 1 a1439 2 else p += rc; d1444 2 a1445 2 } else ++p; d1451 1 a1451 2 else p += rc; @ 1.31 log @THLs review @ text @d465 1 a465 2 else ++p; d481 1 a481 2 else ++p; d712 1 a712 1 else if (rc > 0) { d716 2 a717 2 } else p += rc; d724 1 a724 1 else if (rc > 0) { d764 1 a764 1 else if (rc > 0) { d768 2 a769 2 } else p += rc; d776 1 a776 1 else if (rc > 0) { d932 2 a933 2 } else ++p; d1388 1 a1388 2 else ++p; d1394 1 a1394 2 else p += rc; d1399 2 a1400 2 } else ++p; d1406 1 a1406 2 else p += rc; d1411 2 a1412 2 } else ++p; d1417 4 a1420 6 else { flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; p += rc; } @ 1.30 log @THLs review @ text @d1317 1 a1317 1 char buf[1024]; d1334 1 a1334 1 else if (rc == 0) { d1337 2 a1338 2 } else p += rc; d1351 1 a1351 1 else if (rc == 0) { d1354 2 a1355 2 } else p += rc; d1374 1 a1374 1 else if (rc == 0) { d1377 5 a1381 7 } else p += rc; if (data->begin != NULL) { if (data->begin != data->end) { tokenbuf_free(data); tokenbuf_move(&tmptokbuf, data); } @ 1.29 log @THLs review @ text @d1248 1 a1248 1 /* If the buffer does not life in an allocated buffer, a1285 5 } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; d1287 4 @ 1.28 log @Removed comment from Thomas in accordance with Ralf: We don't want to add further consistency checks, because they're somewhat mood anyway. If a user does not understand the implications of his choice of tokens, there's no way we can guarantee the parser will still work. On the other hand, additional consistency checks might hinder users who know what they're doing from doing it. @ text @d910 3 a912 1 if (!tokenbuf_assign(&res, p, (data->begin + num2) - p)) @ 1.27 log @Removed comment from Thomas in accordance with Ralf: We don't want to introduce a context for the library at this point, because this would complicate the API quite a bit without bringing significant benefits. @ text @a1640 1 //FIXME THL: if(config->varinit == ':' ...any must not be colon > ERROR, varname() will fail a1642 2 //FIXME THL: if(config->varinit == config->startdelimit || ...any with any... @ 1.26 log @THLs review @ text @a238 1 //FIXME THL: shouldn't we use a "libvar" context and do this and the consistency checks only once? This is like a delay-loop ... @ 1.25 log @Implemented var_strerror(). @ text @d526 1 a526 1 else if (rc > 0) { d530 2 a531 2 } else p += rc; d538 1 a538 1 else if (rc > 0) { d542 2 a543 2 } else p += rc; d562 2 a563 2 } else ++p; d571 1 a571 1 else if (rc == 0) { a577 8 } else { /* Initialize result to point back to the original text in the buffer. */ result->begin = begin - 1; result->end = p; result->buffer_size = 0; failed = 1; d579 7 @ 1.24 log @THLs review @ text @d52 50 @ 1.23 log @THLs review @ text @d189 1 @ 1.22 log @Added early initialization of the "result" pointer so that we never leave it unitialized when var_expand() failes. @ text @d618 1 a618 1 else if (rc > 0) { d622 1 a622 1 else if (rc2 == 0) { d625 3 a627 10 else { result->begin = begin; result->end = begin + 1 + rc; result->buffer_size = 0; return 1 + rc; } } else { result->begin = data; result->end = data + len; result->buffer_size = buffer_size; d630 4 d1536 4 a1539 3 } else begin += rc; } else if (rc < 0) d1592 1 d1595 2 @ 1.21 log @Implemented the error-reporting semantics specified in the man page: If var_expand() fails with an error, "result" will contain a pointer to the construct in the input buffer, which's expansion caused the failure. @ text @d1581 4 @ 1.20 log @Added support for the PCRE library: If HAVE_PCREPOSIX is defined at compile time, the PCRE POSIX wrapper functions will be used rather than the system's regular expression engine. @ text @d1515 1 d1554 1 d1556 3 d1595 2 a1596 5 if ((rc = input(input_buf, input_buf + input_len, config, nameclass, lookup, lookup_context, force_expand, &output)) != VAR_OK) { tokenbuf_free(&output); return rc; } d1599 1 a1599 2 return VAR_OK; @ 1.19 log @Replaced the use of the explicit character class type "char class[256]" by an abstract data type "char_class_t". @ text @d35 5 a39 1 #include @ 1.18 log @ops, lookup_context is allowed to be NULL, of course @ text @d78 1 a78 1 d156 1 a156 1 d167 3 a169 1 static void expand_range(char a, char b, char class[256]) d177 1 a177 1 static var_rc_t expand_character_class(const char *desc, char class[256]) d369 1 a369 1 const char[256], var_cb_t, void *, int, tokenbuf_t *); d371 1 a371 1 const char[256], var_cb_t, void *, int, tokenbuf_t *); d389 1 a389 1 const char nameclass[256]) d438 1 a438 1 const char nameclass[256], var_cb_t lookup, d586 1 a586 1 const var_config_t *config, const char nameclass[256], d646 1 a646 1 const char nameclass[256], var_cb_t lookup, d698 1 a698 1 const char nameclass[256], var_cb_t lookup, d1171 1 a1171 1 const var_config_t *config, const char nameclass[256], d1507 1 a1507 1 const char nameclass[256], var_cb_t lookup, d1558 1 a1558 1 char nameclass[256]; d1563 1 a1563 1 if (input_buf == NULL || input_len == 0 || @ 1.17 log @one more hard-coded value vs. symbolic name @ text @d1563 1 a1563 1 lookup == NULL || lookup_context == NULL) @ 1.16 log @be pedantic and use the symbol instead of hard-coded value @ text @d332 1 a332 1 if ((rc = expand_hex(&src, &dst, end)) != 0) @ 1.15 log @Safe time by cleaning up var_expand() myself instead of first writing down the wishes and having Peter do it himself afterwards: - use tokenbuf_init() instead of doing it manually - perform additional argument sanity checks - some minor coding style adjustments @ text @d283 1 a283 1 if ((rc = expand_simple_hex(src, dst, end)) != 0) @ 1.14 log @Rename the return codes VAR_XXX to VAR_ERR_XXX (except for VAR_OK) to be consistent with other OSSP libraries like L2. @ text @d1560 5 a1564 1 /* Expand the class description for valid variable names. */ d1566 1 d1569 3 a1571 2 rc = expand_character_class(config->namechars, nameclass); if (rc != VAR_OK) d1576 4 a1579 5 if (nameclass[(int) config->varinit] || nameclass[(int) config->startdelim] || nameclass[(int) config->enddelim] || nameclass[(int) config->escape]) d1583 3 a1585 6 output.begin = output.end = NULL; output.buffer_size = 0; rc = input(input_buf, input_buf + input_len, config, nameclass, lookup, lookup_context, force_expand, &output); if (rc != VAR_OK) { d1589 1 a1589 1 *result = (char *) output.begin; @ 1.13 log @Cleanup code of token buffer stuff to be consistent with our general style in our OSSP libraries: - rename all xxx_tokenbuf functions to tokenbuf_xxx ("grouping by prefix") - rename tokenbuf to tokenbuf_t (POSIX-like type namespace) - move variable definitions to top of functions (group them together). - glue code comments directly to code (no extra newline) - use the combined "assign and check" style ('if ((var = ...) != NULL)..) @ text @d190 1 a190 1 return VAR_INCORRECT_CLASS_SPEC; d215 1 a215 1 return VAR_INCOMPLETE_OCTAL; d217 1 a217 1 return VAR_INVALID_OCTAL; d221 1 a221 1 return VAR_OCTAL_TOO_LARGE; d251 1 a251 1 return VAR_INCOMPLETE_HEX; d253 1 a253 1 return VAR_INVALID_HEX; d288 1 a288 1 return VAR_INCOMPLETE_GROUPED_HEX; d296 1 a296 1 return VAR_INCOMPLETE_HEX; d313 1 a313 1 return VAR_INCOMPLETE_NAMED_CHARACTER; d378 1 a378 1 return VAR_INCOMPLETE_QUOTED_PAIR; d408 1 a408 1 return VAR_INCOMPLETE_QUOTED_PAIR; d425 1 a425 1 return VAR_INCOMPLETE_QUOTED_PAIR; d460 1 a460 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d471 1 a471 1 rc = VAR_OUT_OF_MEMORY; d483 1 a483 1 rc = VAR_OUT_OF_MEMORY; d495 1 a495 1 rc = VAR_INCOMPLETE_VARIABLE_SPEC; d503 1 a503 1 rc = VAR_INCOMPLETE_VARIABLE_SPEC; d519 1 a519 1 rc = VAR_UNDEFINED_VARIABLE; d560 1 a560 1 rc = VAR_INCOMPLETE_VARIABLE_SPEC; d604 1 a604 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d618 1 a618 1 return VAR_UNDEFINED_VARIABLE; d664 1 a664 1 rc = VAR_OUT_OF_MEMORY; d678 1 a678 1 rc = VAR_OUT_OF_MEMORY; d716 1 a716 1 rc = VAR_OUT_OF_MEMORY; d730 1 a730 1 rc = VAR_OUT_OF_MEMORY; d754 1 a754 1 return VAR_INCORRECT_TRANSPOSE_CLASS_SPEC; d757 1 a757 1 return VAR_OUT_OF_MEMORY; d762 1 a762 1 return VAR_OUT_OF_MEMORY; d787 1 a787 1 rc = VAR_EMPTY_TRANSPOSE_CLASS; d791 1 a791 1 rc = VAR_TRANSPOSE_CLASSES_MISMATCH; d799 1 a799 1 rc = VAR_OUT_OF_MEMORY; d837 1 a837 1 return VAR_OFFSET_OUT_OF_BOUNDS; d845 1 a845 1 return VAR_OUT_OF_MEMORY; d850 1 a850 1 return VAR_RANGE_OUT_OF_BOUNDS; d852 1 a852 1 return VAR_OUT_OF_MEMORY; d855 1 a855 1 return VAR_OFFSET_LOGIC_ERROR; d857 1 a857 1 return VAR_RANGE_OUT_OF_BOUNDS; d859 1 a859 1 return VAR_OUT_OF_MEMORY; d879 1 a879 1 return VAR_INCOMPLETE_QUOTED_PAIR; d885 1 a885 1 return VAR_OUT_OF_MEMORY; d892 1 a892 1 return VAR_UNKNOWN_QUOTED_PAIR_IN_REPLACE; d898 1 a898 1 return VAR_SUBMATCH_OUT_OF_RANGE; d903 1 a903 1 return VAR_OUT_OF_MEMORY; d908 1 a908 1 return VAR_OUT_OF_MEMORY; d927 1 a927 1 return VAR_EMPTY_SEARCH_STRING; d941 1 a941 1 return VAR_UNKNOWN_REPLACE_FLAG; d959 1 a959 1 return VAR_OUT_OF_MEMORY; d969 1 a969 1 return VAR_OUT_OF_MEMORY; d991 1 a991 1 return VAR_OUT_OF_MEMORY; d995 1 a995 1 return VAR_OUT_OF_MEMORY; d1005 1 a1005 1 return VAR_INVALID_REGEX_IN_REPLACE; d1036 1 a1036 1 return VAR_OUT_OF_MEMORY; d1065 1 a1065 1 return VAR_EMPTY_PADDING_FILL_STRING; d1076 1 a1076 1 return VAR_OUT_OF_MEMORY; d1082 1 a1082 1 return VAR_OUT_OF_MEMORY; d1092 1 a1092 1 return VAR_OUT_OF_MEMORY; d1100 1 a1100 1 return VAR_OUT_OF_MEMORY; d1105 1 a1105 1 return VAR_OUT_OF_MEMORY; d1121 1 a1121 1 return VAR_OUT_OF_MEMORY; d1129 1 a1129 1 return VAR_OUT_OF_MEMORY; d1137 1 a1137 1 return VAR_OUT_OF_MEMORY; d1148 1 a1148 1 return VAR_OUT_OF_MEMORY; d1155 1 a1155 1 return VAR_OUT_OF_MEMORY; d1200 1 a1200 1 rc = VAR_OUT_OF_MEMORY; d1216 1 a1216 1 rc = VAR_OUT_OF_MEMORY; d1230 1 a1230 1 rc = VAR_MISSING_START_OFFSET; d1246 1 a1246 1 rc = VAR_INVALID_OFFSET_DELIMITER; d1268 1 a1268 1 rc = VAR_OUT_OF_MEMORY; d1282 1 a1282 1 rc = VAR_MISSING_PARAMETER_IN_COMMAND; d1299 1 a1299 1 rc = VAR_MISSING_PARAMETER_IN_COMMAND; d1322 1 a1322 1 rc = VAR_MISSING_PARAMETER_IN_COMMAND; d1338 1 a1338 1 return VAR_MALFORMATTED_REPLACE; d1350 1 a1350 1 rc = VAR_MALFORMATTED_REPLACE; d1363 1 a1363 1 rc = VAR_MALFORMATTED_REPLACE; d1389 1 a1389 1 return VAR_MALFORMATTED_TRANSPOSE; d1401 1 a1401 1 rc = VAR_MALFORMATTED_TRANSPOSE; d1414 1 a1414 1 rc = VAR_MALFORMATTED_TRANSPOSE; d1431 1 a1431 1 return VAR_MALFORMATTED_PADDING; d1437 1 a1437 1 rc = VAR_MISSING_PADDING_WIDTH; d1447 1 a1447 1 rc = VAR_MALFORMATTED_PADDING; d1460 1 a1460 1 rc = VAR_MALFORMATTED_PADDING; d1466 1 a1466 1 rc = VAR_MALFORMATTED_PADDING; d1479 1 a1479 1 return VAR_UNKNOWN_COMMAND_CHAR; d1518 1 a1518 1 rc = VAR_OUT_OF_MEMORY; d1530 1 a1530 1 rc = VAR_OUT_OF_MEMORY; d1540 1 a1540 1 rc = VAR_INPUT_ISNT_TEXT_NOR_VARIABLE; d1575 1 a1575 1 return VAR_INVALID_CONFIGURATION; @ 1.12 log @be consistent: every function has at least one 'return' @ text @d48 1 a48 1 /* Routines for manipulation of tokenbufs. */ d50 1 a50 1 #define VAR_INITIAL_BUFFER_SIZE 64 d56 1 a56 1 } tokenbuf; d58 1 a58 1 static void init_tokenbuf(tokenbuf *buf) d60 2 a61 1 buf->begin = buf->end = NULL; d66 1 a66 1 static void move_tokenbuf(tokenbuf *src, tokenbuf *dst) d71 1 a71 1 init_tokenbuf(src); d75 1 a75 1 static int assign_to_tokenbuf(tokenbuf *buf, const char *data, size_t len) d77 3 a79 9 char *p = malloc(len + 1); if (p) { memcpy(p, data, len); buf->begin = p; buf->end = p + len; buf->buffer_size = len + 1; *((char *) (buf->end)) = '\0'; return 1; } else d81 6 d89 1 a89 2 static int append_to_tokenbuf(tokenbuf *output, const char *data, size_t len) d93 1 a96 1 d98 1 a98 2 if ((output->begin = output->end = malloc(VAR_INITIAL_BUFFER_SIZE)) == NULL) d100 1 a100 2 else output->buffer_size = VAR_INITIAL_BUFFER_SIZE; d103 1 a103 3 /* Does the token contain text, but no buffer has been allocated yet? */ a106 1 a110 1 d113 7 a119 11 else { char *tmp = malloc(output->end - output->begin + len + 1); if (!tmp) return 0; memcpy(tmp, output->begin, output->end - output->begin); output->buffer_size = output->end - output->begin; output->begin = tmp; output->end = tmp + output->buffer_size; output->buffer_size += len + 1; } a123 1 d128 2 a129 4 } while ((new_size - (output->end - output->begin)) <= len); new_buffer = realloc((char *) output->begin, new_size); if (new_buffer == NULL) a136 1 d143 1 a143 1 static void free_tokenbuf(tokenbuf *buf) d146 1 a146 1 free((char *) buf->begin); d152 1 a152 1 static size_t tokenbuf2int(tokenbuf *number) d155 3 a157 1 size_t num = 0; d367 1 a367 1 const char[256], var_cb_t, void *, int, tokenbuf *); d369 1 a369 1 const char[256], var_cb_t, void *, int, tokenbuf *); d438 1 a438 1 tokenbuf *result) d445 2 a446 2 tokenbuf name; tokenbuf tmp; d450 3 a452 3 init_tokenbuf(&name); init_tokenbuf(&tmp); init_tokenbuf(result); d470 1 a470 1 if (!append_to_tokenbuf(&name, p, rc)) { d482 1 a482 1 if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin)) { d542 1 a542 1 free_tokenbuf(&tmp); d570 2 a571 2 free_tokenbuf(&name); free_tokenbuf(&tmp); d577 3 a579 3 free_tokenbuf(&name); free_tokenbuf(&tmp); free_tokenbuf(result); d586 1 a586 1 int force_expand, tokenbuf *result) d596 1 a596 1 init_tokenbuf(result); d646 1 a646 1 tokenbuf *result) d649 1 a649 1 tokenbuf tmp; d652 2 a653 2 init_tokenbuf(result); init_tokenbuf(&tmp); d663 1 a663 1 if (!append_to_tokenbuf(result, p, rc)) { d676 1 a676 1 if (!append_to_tokenbuf d685 1 a685 1 free_tokenbuf(&tmp); d689 2 a690 2 free_tokenbuf(&tmp); free_tokenbuf(result); d698 1 a698 1 tokenbuf *result) d701 1 a701 1 tokenbuf tmp; d704 2 a705 2 init_tokenbuf(result); init_tokenbuf(&tmp); d715 1 a715 1 if (!append_to_tokenbuf(result, p, rc)) { d728 1 a728 1 if (!append_to_tokenbuf d737 1 a737 1 free_tokenbuf(&tmp); d741 2 a742 2 free_tokenbuf(&tmp); free_tokenbuf(result); d747 1 a747 1 static int expand_class_description(tokenbuf *src, tokenbuf *dst) d756 1 a756 1 if (!append_to_tokenbuf(dst, (char *) &c, 1)) d761 1 a761 1 if (!append_to_tokenbuf(dst, p, 1)) d770 2 a771 2 static int transpose(tokenbuf *data, tokenbuf *search, tokenbuf *replace) d773 1 a773 1 tokenbuf srcclass, dstclass; d778 2 a779 2 init_tokenbuf(&srcclass); init_tokenbuf(&dstclass); d796 2 a797 2 tokenbuf tmp; if (!assign_to_tokenbuf d802 1 a802 1 move_tokenbuf(&tmp, data); d814 2 a815 2 free_tokenbuf(&srcclass); free_tokenbuf(&dstclass); d819 4 a822 4 free_tokenbuf(search); free_tokenbuf(replace); free_tokenbuf(&srcclass); free_tokenbuf(&dstclass); d826 2 a827 2 static int cut_out_offset(tokenbuf *data, tokenbuf *number1, tokenbuf *number2, int isrange) d829 1 a829 1 tokenbuf res; d831 2 a832 2 size_t num1 = tokenbuf2int(number1); size_t num2 = tokenbuf2int(number2); d844 1 a844 1 if (!assign_to_tokenbuf(&res, p, data->end - p)) d851 1 a851 1 if (!assign_to_tokenbuf(&res, p, num2)) d858 1 a858 1 if (!assign_to_tokenbuf(&res, p, (data->begin + num2) - p)) d862 2 a863 2 free_tokenbuf(data); move_tokenbuf(&res, data); d867 2 a868 2 static int expand_regex_replace(const char *data, tokenbuf *orig, regmatch_t *pmatch, tokenbuf *expanded) d873 1 a873 1 init_tokenbuf(expanded); d878 1 a878 1 free_tokenbuf(expanded); d883 2 a884 2 if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); d891 1 a891 1 free_tokenbuf(expanded); d897 1 a897 1 free_tokenbuf(expanded); d900 1 a900 1 if (!append_to_tokenbuf(expanded, data + pmatch[i].rm_so, d902 1 a902 1 free_tokenbuf(expanded); d906 2 a907 2 if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); d917 2 a918 2 static int search_and_replace(tokenbuf *data, tokenbuf *search, tokenbuf *replace, tokenbuf *flags) d946 2 a947 2 tokenbuf tmp; init_tokenbuf(&tmp); d957 2 a958 2 if (!append_to_tokenbuf(&tmp, p, 1)) { free_tokenbuf(&tmp); d963 1 a963 1 append_to_tokenbuf(&tmp, replace->begin, d967 2 a968 2 if (!append_to_tokenbuf(&tmp, p, data->end - p)) { free_tokenbuf(&tmp); d976 2 a977 2 free_tokenbuf(data); move_tokenbuf(&tmp, data); d979 3 a981 3 tokenbuf tmp; tokenbuf mydata; tokenbuf myreplace; d989 1 a989 1 if (!assign_to_tokenbuf d992 1 a992 1 if (!assign_to_tokenbuf d994 1 a994 1 free_tokenbuf(&tmp); d1002 1 a1002 1 free_tokenbuf(&tmp); d1004 1 a1004 1 free_tokenbuf(&mydata); d1019 1 a1019 1 append_to_tokenbuf(&tmp, p, mydata.end - p); d1025 2 a1026 2 free_tokenbuf(&tmp); free_tokenbuf(&mydata); d1029 2 a1030 2 if (!append_to_tokenbuf(&tmp, p, pmatch[0].rm_so) || !append_to_tokenbuf(&tmp, myreplace.begin, d1033 3 a1035 3 free_tokenbuf(&tmp); free_tokenbuf(&mydata); free_tokenbuf(&myreplace); d1039 1 a1039 1 free_tokenbuf(&myreplace); d1042 1 a1042 1 append_to_tokenbuf(&tmp, p, mydata.end - p); d1049 3 a1051 3 free_tokenbuf(data); move_tokenbuf(&tmp, data); free_tokenbuf(&mydata); d1057 1 a1057 1 static int padding(tokenbuf *data, tokenbuf *widthstr, tokenbuf *fill, d1060 2 a1061 2 tokenbuf result; size_t width = tokenbuf2int(widthstr); d1067 1 a1067 1 init_tokenbuf(&result); d1074 1 a1074 1 if (!append_to_tokenbuf d1081 1 a1081 1 if (!append_to_tokenbuf(data, fill->begin, i)) d1089 1 a1089 1 if (!append_to_tokenbuf d1091 1 a1091 1 free_tokenbuf(&result); d1098 2 a1099 2 if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); d1102 1 a1102 1 if (!append_to_tokenbuf d1104 1 a1104 1 free_tokenbuf(&result); d1108 2 a1109 2 free_tokenbuf(data); move_tokenbuf(&result, data); d1118 1 a1118 1 if (!append_to_tokenbuf d1120 1 a1120 1 free_tokenbuf(&result); d1127 2 a1128 2 if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); d1134 1 a1134 1 if (!append_to_tokenbuf d1136 1 a1136 1 free_tokenbuf(&result); d1145 1 a1145 1 if (!append_to_tokenbuf d1147 1 a1147 1 free_tokenbuf(&result); d1153 2 a1154 2 if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); d1160 2 a1161 2 free_tokenbuf(data); move_tokenbuf(&result, data); d1171 1 a1171 1 tokenbuf *data) d1174 3 a1176 3 tokenbuf tmptokbuf; tokenbuf search, replace, flags; tokenbuf number1, number2; d1180 6 a1185 6 init_tokenbuf(&tmptokbuf); init_tokenbuf(&search); init_tokenbuf(&replace); init_tokenbuf(&flags); init_tokenbuf(&number1); init_tokenbuf(&number2); d1198 1 a1198 1 if (!assign_to_tokenbuf d1214 1 a1214 1 if (!assign_to_tokenbuf d1266 2 a1267 2 free_tokenbuf(data); if (!assign_to_tokenbuf(data, buf, strlen(buf))) { d1287 2 a1288 2 free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); d1305 2 a1306 2 free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); d1308 1 a1308 1 free_tokenbuf(data); d1328 2 a1329 2 free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); d1484 6 a1489 6 free_tokenbuf(&tmptokbuf); free_tokenbuf(&search); free_tokenbuf(&replace); free_tokenbuf(&flags); free_tokenbuf(&number1); free_tokenbuf(&number2); d1493 7 a1499 7 free_tokenbuf(data); free_tokenbuf(&tmptokbuf); free_tokenbuf(&search); free_tokenbuf(&replace); free_tokenbuf(&flags); free_tokenbuf(&number1); free_tokenbuf(&number2); d1507 1 a1507 1 tokenbuf *output) d1510 1 a1510 1 tokenbuf result; d1512 1 a1512 1 init_tokenbuf(&result); d1517 1 a1517 1 if (!append_to_tokenbuf(output, begin, rc)) { d1528 1 a1528 1 if (!append_to_tokenbuf d1547 1 a1547 1 free_tokenbuf(&result); d1558 1 a1558 1 tokenbuf output; d1584 1 a1584 1 free_tokenbuf(&output); @ 1.11 log @use consistent style for pointers @ text @d62 1 d71 1 d163 1 @ 1.10 log @Quickly get rid of tabs before reviewing and editing starts @ text @d58 1 a58 1 static void init_tokenbuf(tokenbuf * buf) d64 1 a64 1 static void move_tokenbuf(tokenbuf * src, tokenbuf * dst) d72 1 a72 1 static int assign_to_tokenbuf(tokenbuf * buf, const char *data, size_t len) d86 1 a86 1 static int append_to_tokenbuf(tokenbuf * output, const char *data, d149 1 a149 1 memcpy((char *) output->end, data, len); d151 1 a151 1 *((char *) output->end) = '\0'; d155 1 a155 1 static void free_tokenbuf(tokenbuf * buf) d163 1 a163 1 static size_t tokenbuf2int(tokenbuf * number) d179 1 a179 1 class[(int) a] = 1; d411 1 a411 1 const var_config_t * config) d426 1 a426 1 const var_config_t * config) d444 1 a444 1 const var_config_t * config, d447 1 a447 1 tokenbuf * result) d593 1 a593 1 const var_config_t * config, const char nameclass[256], d595 1 a595 1 int force_expand, tokenbuf * result) d652 1 a652 1 const var_config_t * config, d655 1 a655 1 tokenbuf * result) d704 1 a704 1 const var_config_t * config, d707 1 a707 1 tokenbuf * result) d756 1 a756 1 static int expand_class_description(tokenbuf * src, tokenbuf * dst) d779 2 a780 2 static int transpose(tokenbuf * data, tokenbuf * search, tokenbuf * replace) d835 2 a836 2 static int cut_out_offset(tokenbuf * data, tokenbuf * number1, tokenbuf * number2, int isrange) d876 2 a877 2 static int expand_regex_replace(const char *data, tokenbuf * orig, regmatch_t * pmatch, tokenbuf * expanded) d926 2 a927 2 static int search_and_replace(tokenbuf * data, tokenbuf * search, tokenbuf * replace, tokenbuf * flags) d1066 1 a1066 1 static int padding(tokenbuf * data, tokenbuf * widthstr, tokenbuf * fill, d1178 1 a1178 1 const var_config_t * config, const char nameclass[256], d1180 1 a1180 1 tokenbuf * data) d1513 1 a1513 1 const var_config_t * config, d1516 1 a1516 1 tokenbuf * output) d1561 1 a1561 1 char **result, size_t * result_len, d1563 1 a1563 1 const var_config_t * config, int force_expand) @ 1.9 log @Added cast to int for isdigit() parameter to avoid compile-time warning. @ text @d41 5 a45 5 '$', /* varinit */ '{', /* startdelim */ '}', /* enddelim */ '\\', /* escape */ "a-zA-Z0-9_" /* namechars */ d76 6 a81 6 memcpy(p, data, len); buf->begin = p; buf->end = p + len; buf->buffer_size = len + 1; *((char *) (buf->end)) = '\0'; return 1; d83 1 a83 1 return 0; d87 1 a87 1 size_t len) d96 5 a100 5 if ((output->begin = output->end = malloc(VAR_INITIAL_BUFFER_SIZE)) == NULL) return 0; else output->buffer_size = VAR_INITIAL_BUFFER_SIZE; d107 2 a108 2 /* Check whether data borders to output. If, we can append simly by increasing the end pointer. */ d110 18 a127 18 if (output->end == data) { output->end += len; return 1; } /* OK, so copy the contents of output into an allocated buffer so that we can append that way. */ else { char *tmp = malloc(output->end - output->begin + len + 1); if (!tmp) return 0; memcpy(tmp, output->begin, output->end - output->begin); output->buffer_size = output->end - output->begin; output->begin = tmp; output->end = tmp + output->buffer_size; output->buffer_size += len + 1; } d134 11 a144 11 new_size = output->buffer_size; do { new_size *= 2; } while ((new_size - (output->end - output->begin)) <= len); new_buffer = realloc((char *) output->begin, new_size); if (new_buffer == NULL) return 0; output->end = new_buffer + (output->end - output->begin); output->begin = new_buffer; output->buffer_size = new_size; d158 1 a158 1 free((char *) buf->begin); d168 2 a169 2 num *= 10; num += *p - '0'; d179 1 a179 1 class[(int) a] = 1; d191 1 a191 1 class[i] = 0; d197 9 a205 9 if (desc[1] == '-' && desc[2] != '\0') { if (desc[0] > desc[2]) return VAR_INCORRECT_CLASS_SPEC; expand_range(desc[0], desc[2], class); desc += 3; } else { class[(int) *desc] = 1; ++desc; } d214 1 a214 1 return 1; d216 1 a216 1 return 0; d224 1 a224 1 return VAR_INCOMPLETE_OCTAL; d226 1 a226 1 return VAR_INVALID_OCTAL; d230 1 a230 1 return VAR_OCTAL_TOO_LARGE; d248 2 a249 2 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return 1; d251 1 a251 1 return 0; d255 1 a255 1 const char *end) d260 1 a260 1 return VAR_INCOMPLETE_HEX; d262 1 a262 1 return VAR_INVALID_HEX; d265 1 a265 1 c = **src - '0'; d267 1 a267 1 c = **src - 'a' + 10; d269 1 a269 1 c = **src - 'A' + 10; d275 1 a275 1 c += **src - '0'; d277 1 a277 1 c += **src - 'a' + 10; d279 1 a279 1 c += **src - 'A' + 10; d287 1 a287 1 const char *end) d292 3 a294 3 if ((rc = expand_simple_hex(src, dst, end)) != 0) return rc; ++(*src); d297 1 a297 1 return VAR_INCOMPLETE_GROUPED_HEX; d305 1 a305 1 return VAR_INCOMPLETE_HEX; d307 2 a308 2 ++(*src); return expand_grouped_hex(src, dst, end); d310 1 a310 1 return expand_simple_hex(src, dst, end); d314 1 a314 1 int unescape_all) d320 48 a367 48 if (*src == '\\') { if (++src == end) return VAR_INCOMPLETE_NAMED_CHARACTER; switch (*src) { case '\\': if (!unescape_all) { *dst++ = '\\'; } *dst++ = '\\'; break; case 'n': *dst++ = '\n'; break; case 't': *dst++ = '\t'; break; case 'r': *dst++ = '\r'; break; case 'x': ++src; if ((rc = expand_hex(&src, &dst, end)) != 0) return rc; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (end - src >= 3 && isdigit((int)src[1]) && isdigit((int)src[2])) { if ((rc = expand_octal(&src, &dst, end)) != 0) return rc; break; } default: if (!unescape_all) { *dst++ = '\\'; } *dst++ = *src; } ++src; } else *dst++ = *src++; d376 1 a376 1 const char[256], var_cb_t, void *, int, tokenbuf *); d378 1 a378 1 const char[256], var_cb_t, void *, int, tokenbuf *); d381 1 a381 1 char escape) d385 6 a390 6 if (*p == escape) { if (p + 1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } d396 1 a396 1 const char nameclass[256]) d411 1 a411 1 const var_config_t * config) d415 6 a420 6 if (*p == config->escape) { if (p + 1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } d426 1 a426 1 const var_config_t * config) d430 8 a437 8 p != end && *p != config->varinit && *p != config->enddelim && *p != ':'; ++p) { if (*p == config->escape) { if (p + 1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } d444 4 a447 4 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * result) d466 1 a466 1 return 0; d469 1 a469 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d475 22 a496 22 rc = varname(p, end, nameclass); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(&name, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } d504 2 a505 2 rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; d512 2 a513 2 rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; d515 1 a515 1 ++p; d520 1 a520 1 &data, &len, &buffer_size); d522 1 a522 1 goto error_return; d524 2 a525 2 /* The variable is undefined. What we'll do now depends on the force_expand flag. */ d527 12 a538 12 if (force_expand) { rc = VAR_UNDEFINED_VARIABLE; goto error_return; } else { /* Initialize result to point back to the original text in the buffer. */ result->begin = begin - 1; result->end = p; result->buffer_size = 0; failed = 1; } d540 2 a541 2 /* The preliminary result is the contents of the variable. This may be modified by the commands that may follow. */ d543 3 a545 3 result->begin = data; result->end = data + len; result->buffer_size = buffer_size; d549 1 a549 1 /* Parse and execute commands. */ d551 24 a574 24 free_tokenbuf(&tmp); --p; while (p != end && *p == ':') { ++p; if (!failed) rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result); else rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; p += rc; if (failed) result->end += rc; } if (p == end || *p != config->enddelim) { rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } ++p; if (failed) ++result->end; d593 3 a595 3 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * result) d610 1 a610 1 return 0; d613 1 a613 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d620 1 a620 1 return rc; d622 18 a639 18 rc2 = (*lookup) (lookup_context, p, rc, &data, &len, &buffer_size); if (rc2 < 0) return rc2; else if (rc2 == 0) { if (force_expand) return VAR_UNDEFINED_VARIABLE; else { result->begin = begin; result->end = begin + 1 + rc; result->buffer_size = 0; return 1 + rc; } } else { result->begin = data; result->end = data + len; result->buffer_size = buffer_size; return 1 + rc; } d645 1 a645 1 force_expand, result); d647 1 a647 1 ++rc; d652 4 a655 4 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * result) d665 1 a665 1 return 0; d668 23 a690 23 rc = exptext(p, end, config); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(result, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { p += rc; if (!append_to_tokenbuf (result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } d704 4 a707 4 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * result) d717 1 a717 1 return 0; d720 23 a742 23 rc = substext(p, end, config); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(result, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { p += rc; if (!append_to_tokenbuf (result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } d761 14 a774 14 if ((src->end - p) >= 3 && p[1] == '-') { if (*p > p[2]) return VAR_INCORRECT_TRANSPOSE_CLASS_SPEC; for (c = *p, d = p[2]; c <= d; ++c) { if (!append_to_tokenbuf(dst, (char *) &c, 1)) return VAR_OUT_OF_MEMORY; } p += 3; } else { if (!append_to_tokenbuf(dst, p, 1)) return VAR_OUT_OF_MEMORY; else ++p; } d780 1 a780 1 tokenbuf * replace) d791 1 a791 1 goto error_return; d793 1 a793 1 goto error_return; d796 2 a797 2 rc = VAR_EMPTY_TRANSPOSE_CLASS; goto error_return; d800 2 a801 2 rc = VAR_TRANSPOSE_CLASSES_MISMATCH; goto error_return; d805 7 a811 7 tokenbuf tmp; if (!assign_to_tokenbuf (&tmp, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } move_tokenbuf(&tmp, data); d815 6 a820 6 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) { if (*p == srcclass.begin[i]) { *((char *) p) = dstclass.begin[i]; break; } } d836 1 a836 1 tokenbuf * number2, int isrange) d846 1 a846 1 return VAR_OFFSET_OUT_OF_BOUNDS; d848 1 a848 1 p = data->begin + num1; d853 17 a869 17 if (!assign_to_tokenbuf(&res, p, data->end - p)) return VAR_OUT_OF_MEMORY; } else { /* OK, then use num2. */ if (isrange) { if ((p + num2) > data->end) return VAR_RANGE_OUT_OF_BOUNDS; if (!assign_to_tokenbuf(&res, p, num2)) return VAR_OUT_OF_MEMORY; } else { if (num2 < num1) return VAR_OFFSET_LOGIC_ERROR; if ((data->begin + num2) > data->end) return VAR_RANGE_OUT_OF_BOUNDS; if (!assign_to_tokenbuf(&res, p, (data->begin + num2) - p)) return VAR_OUT_OF_MEMORY; } d877 1 a877 1 regmatch_t * pmatch, tokenbuf * expanded) d885 36 a920 36 if (*p == '\\') { if (orig->end - p <= 1) { free_tokenbuf(expanded); return VAR_INCOMPLETE_QUOTED_PAIR; } else ++p; if (*p == '\\') { if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } ++p; continue; } if (!isdigit((int)*p)) { free_tokenbuf(expanded); return VAR_UNKNOWN_QUOTED_PAIR_IN_REPLACE; } i = *p - '0'; ++p; if (pmatch[i].rm_so == -1) { free_tokenbuf(expanded); return VAR_SUBMATCH_OUT_OF_RANGE; } if (!append_to_tokenbuf(expanded, data + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } } else { if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } ++p; } d927 1 a927 1 tokenbuf * replace, tokenbuf * flags) d936 1 a936 1 return VAR_EMPTY_SEARCH_STRING; d939 13 a951 13 switch (tolower(*p)) { case 'i': case_insensitive = 1; break; case 'g': global = 1; break; case 't': no_regex = 1; break; default: return VAR_UNKNOWN_REPLACE_FLAG; } d955 2 a956 2 tokenbuf tmp; init_tokenbuf(&tmp); d958 26 a983 26 for (p = data->begin; p != data->end;) { if (case_insensitive) rc = strncasecmp(p, search->begin, search->end - search->begin); else rc = strncmp(p, search->begin, search->end - search->begin); if (rc != 0) { /* no match, copy character */ if (!append_to_tokenbuf(&tmp, p, 1)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } ++p; } else { append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin); p += search->end - search->begin; if (!global) { if (!append_to_tokenbuf(&tmp, p, data->end - p)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } break; } } } d985 2 a986 2 free_tokenbuf(data); move_tokenbuf(&tmp, data); d988 73 a1060 73 tokenbuf tmp; tokenbuf mydata; tokenbuf myreplace; regex_t preg; regmatch_t pmatch[10]; int regexec_flag; /* Copy the pattern and the data to our own buffer to make sure they're terminated with a null byte. */ if (!assign_to_tokenbuf (&tmp, search->begin, search->end - search->begin)) return VAR_OUT_OF_MEMORY; if (!assign_to_tokenbuf (&mydata, data->begin, data->end - data->begin)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } /* Compile the pattern. */ rc = regcomp(&preg, tmp.begin, REG_EXTENDED | ((case_insensitive) ? REG_ICASE : 0)); free_tokenbuf(&tmp); if (rc != 0) { free_tokenbuf(&mydata); return VAR_INVALID_REGEX_IN_REPLACE; } /* Match the pattern and create the result string in the tmp buffer. */ for (p = mydata.begin; p != mydata.end;) { if (p == mydata.begin || p[-1] == '\n') regexec_flag = 0; else regexec_flag = REG_NOTBOL; if (regexec (&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH) { append_to_tokenbuf(&tmp, p, mydata.end - p); break; } else { rc = expand_regex_replace(p, replace, pmatch, &myreplace); if (rc != VAR_OK) { regfree(&preg); free_tokenbuf(&tmp); free_tokenbuf(&mydata); return rc; } if (!append_to_tokenbuf(&tmp, p, pmatch[0].rm_so) || !append_to_tokenbuf(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { regfree(&preg); free_tokenbuf(&tmp); free_tokenbuf(&mydata); free_tokenbuf(&myreplace); return VAR_OUT_OF_MEMORY; } else { p += (pmatch[0].rm_eo > 0) ? pmatch[0].rm_eo : 1; free_tokenbuf(&myreplace); } if (!global) { append_to_tokenbuf(&tmp, p, mydata.end - p); break; } } } regfree(&preg); free_tokenbuf(data); move_tokenbuf(&tmp, data); free_tokenbuf(&mydata); d1067 1 a1067 1 char position) d1074 1 a1074 1 return VAR_EMPTY_PADDING_FILL_STRING; d1079 14 a1092 14 i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while (i > 0) { if (!append_to_tokenbuf (data, fill->begin, fill->end - fill->begin)) return VAR_OUT_OF_MEMORY; --i; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!append_to_tokenbuf(data, fill->begin, i)) return VAR_OUT_OF_MEMORY; } d1094 26 a1119 26 i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while (i > 0) { if (!append_to_tokenbuf (&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } if (!append_to_tokenbuf (&result, data->begin, data->end - data->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } free_tokenbuf(data); move_tokenbuf(&result, data); } d1121 51 a1171 51 i = (width - (data->end - data->begin)) / 2; if (i > 0) { /* Create the prefix. */ i = i / (fill->end - fill->begin); while (i > 0) { if (!append_to_tokenbuf (&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Append the actual data string. */ if (!append_to_tokenbuf (&result, data->begin, data->end - data->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Append the suffix. */ i = width - (result.end - result.begin); i = i / (fill->end - fill->begin); while (i > 0) { if (!append_to_tokenbuf (&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = width - (result.end - result.begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Move string from temporary buffer to data buffer. */ free_tokenbuf(data); move_tokenbuf(&result, data); } d1178 3 a1180 3 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * data) d1197 1 a1197 1 return 0; d1200 286 a1485 286 case 'l': /* Turn data to lowercase. */ if (data->begin) { char *ptr; /* If the buffer does not life in an allocated buffer, we have to copy it before modifying the contents. */ if (data->buffer_size == 0) { if (!assign_to_tokenbuf (data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char *) data->begin; ptr != data->end; ++ptr) *ptr = tolower(*ptr); } ++p; break; case 'u': /* Turn data to uppercase. */ if (data->begin) { char *ptr; if (data->buffer_size == 0) { if (!assign_to_tokenbuf (data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char *) data->begin; ptr != data->end; ++ptr) *ptr = toupper(*ptr); } ++p; break; case 'o': /* Cut out substrings. */ ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_START_OFFSET; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p == ',') { isrange = 0; ++p; } else if (*p == '-') { isrange = 1; ++p; } else { rc = VAR_INVALID_OFFSET_DELIMITER; goto error_return; } rc = number(p, end); number2.begin = p; number2.end = p + rc; number2.buffer_size = 0; p += rc; if (data->begin) { rc = cut_out_offset(data, &number1, &number2, isrange); if (rc < 0) goto error_return; } break; case '#': /* Substitute length of the string. */ if (data->begin) { char buf[1024]; sprintf(buf, "%d", data->end - data->begin); free_tokenbuf(data); if (!assign_to_tokenbuf(data, buf, strlen(buf))) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } ++p; break; case '-': /* Substitute parameter if data is empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL && data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } break; case '*': /* Return "" if data is not empty, parameter otherwise. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } else { free_tokenbuf(data); data->begin = data->end = ""; data->buffer_size = 0; } } break; case '+': /* Substitute parameter if data is not empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin != data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } } break; case 's': /* Search and replace. */ ++p; if (*p != '/') return VAR_MALFORMATTED_REPLACE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = exptext(p, end, config); if (rc < 0) goto error_return; else { flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; p += rc; } if (data->begin) { rc = search_and_replace(data, &search, &replace, &flags); if (rc < 0) goto error_return; } break; case 'y': /* Transpose characters from class A to class B. */ ++p; if (*p != '/') return VAR_MALFORMATTED_TRANSPOSE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; if (data->begin) { rc = transpose(data, &search, &replace); if (rc < 0) goto error_return; } break; case 'p': /* Padding. */ ++p; if (*p != '/') return VAR_MALFORMATTED_PADDING; else ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_PADDING_WIDTH; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (*p != 'l' && *p != 'c' && *p != 'r') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (data->begin) { rc = padding(data, &number1, &replace, p[-1]); if (rc < 0) goto error_return; } break; d1488 1 a1488 1 return VAR_UNKNOWN_COMMAND_CHAR; d1513 4 a1516 4 const var_config_t * config, const char nameclass[256], var_cb_t lookup, void *lookup_context, int force_expand, tokenbuf * output) d1524 21 a1544 21 rc = text(begin, end, config->varinit, config->escape); if (rc > 0) { if (!append_to_tokenbuf(output, begin, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } begin += rc; } else if (rc < 0) goto error_return; rc = variable(begin, end, config, nameclass, lookup, lookup_context, force_expand, &result); if (rc > 0) { if (!append_to_tokenbuf (output, result.begin, result.end - result.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else begin += rc; } else if (rc < 0) goto error_return; d1549 2 a1550 2 rc = VAR_INPUT_ISNT_TEXT_NOR_VARIABLE; goto error_return; d1561 3 a1563 3 char **result, size_t * result_len, var_cb_t lookup, void *lookup_context, const var_config_t * config, int force_expand) d1572 1 a1572 1 config = &var_config_default; d1575 1 a1575 1 return rc; d1581 4 a1584 4 nameclass[(int) config->startdelim] || nameclass[(int) config->enddelim] || nameclass[(int) config->escape]) return VAR_INVALID_CONFIGURATION; d1591 1 a1591 1 lookup, lookup_context, force_expand, &output); d1593 2 a1594 2 free_tokenbuf(&output); return rc; @ 1.8 log @Reformatted the sources to Kernighan & Ritchie style according to the wishes of Ralf. @ text @d354 1 a354 1 if (end - src >= 3 && isdigit(src[1]) && isdigit(src[2])) { d406 1 a406 1 for (p = begin; p != end && isdigit(*p); ++p); d899 1 a899 1 if (!isdigit(*p)) { @ 1.7 log @Implemented submatching in regular expressions and added the appropriate test cases. @ text @d40 7 a46 8 const var_config_t var_config_default = { '$', /* varinit */ '{', /* startdelim */ '}', /* enddelim */ '\\', /* escape */ "a-zA-Z0-9_" /* namechars */ }; d52 3 a54 4 typedef struct { const char* begin; const char* end; d56 1 a56 2 } tokenbuf; d58 2 a59 2 static void init_tokenbuf(tokenbuf* buf) { d62 1 a62 1 } d64 2 a65 2 static void move_tokenbuf(tokenbuf* src, tokenbuf* dst) { d70 1 a70 1 } d72 18 a89 19 static int assign_to_tokenbuf(tokenbuf* buf, const char* data, size_t len) { char* p = malloc(len+1); if (p) { memcpy(p, data, len); buf->begin = p; buf->end = p + len; buf->buffer_size = len + 1; *((char*)(buf->end)) = '\0'; return 1; } else return 0; } static int append_to_tokenbuf(tokenbuf* output, const char* data, size_t len) { char* new_buffer; d95 7 a101 7 if (output->begin == NULL) { if ((output->begin = output->end = malloc(VAR_INITIAL_BUFFER_SIZE)) == NULL) return 0; else output->buffer_size = VAR_INITIAL_BUFFER_SIZE; } d106 23 a128 26 if (output->buffer_size == 0) { /* Check whether data borders to output. If, we can append simly by increasing the end pointer. */ if (output->end == data) { output->end += len; return 1; } /* OK, so copy the contents of output into an allocated buffer so that we can append that way. */ else { char* tmp = malloc(output->end - output->begin + len + 1); if (!tmp) return 0; memcpy(tmp, output->begin, output->end - output->begin); output->buffer_size = output->end - output->begin; output->begin = tmp; output->end = tmp + output->buffer_size; output->buffer_size += len + 1; } } d133 13 a145 15 if ((output->buffer_size - (output->end - output->begin)) <= len) { new_size = output->buffer_size; do { new_size *= 2; } while ((new_size - (output->end - output->begin)) <= len); new_buffer = realloc((char*)output->begin, new_size); if (new_buffer == NULL) return 0; output->end = new_buffer + (output->end - output->begin); output->begin = new_buffer; output->buffer_size = new_size; } d149 1 a149 1 memcpy((char*)output->end, data, len); d151 1 a151 1 *((char*)output->end) = '\0'; d153 1 a153 1 } d155 2 a156 2 static void free_tokenbuf(tokenbuf* buf) { d158 1 a158 1 free((char*)buf->begin); d161 1 a161 1 } d163 3 a165 3 static size_t tokenbuf2int(tokenbuf* number) { const char* p; d167 4 a170 5 for (p = number->begin; p != number->end; ++p) { num *= 10; num += *p - '0'; } d172 1 a172 1 } d177 4 a180 5 { do { class[(int)a] = 1; } d182 1 a182 1 } d184 2 a185 2 static var_rc_t expand_character_class(const char* desc, char class[256]) { d191 1 a191 1 class[i] = 0; d196 11 a206 15 while(*desc != '\0') { if (desc[1] == '-' && desc[2] != '\0') { if (desc[0] > desc[2]) return VAR_INCORRECT_CLASS_SPEC; expand_range(desc[0], desc[2], class); desc += 3; } else { class[(int)*desc] = 1; ++desc; } } d209 1 a209 1 } d212 1 a212 1 { d214 1 a214 1 return 1; d216 2 a217 2 return 0; } d219 2 a220 2 static var_rc_t expand_octal(const char** src, char** dst, const char* end) { d224 1 a224 1 return VAR_INCOMPLETE_OCTAL; d226 1 a226 1 return VAR_INVALID_OCTAL; d230 1 a230 1 return VAR_OCTAL_TOO_LARGE; d240 1 a240 1 **dst = (char)c; d243 1 a243 1 } d246 1 a246 1 { d248 2 a249 3 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return 1; d251 2 a252 2 return 0; } d254 3 a256 2 static var_rc_t expand_simple_hex(const char** src, char** dst, const char* end) { d260 1 a260 1 return VAR_INCOMPLETE_HEX; d262 1 a262 1 return VAR_INVALID_HEX; d265 1 a265 1 c = **src - '0'; d267 1 a267 1 c = **src - 'a' + 10; d269 1 a269 1 c = **src - 'A' + 10; d275 1 a275 1 c += **src - '0'; d277 1 a277 1 c += **src - 'a' + 10; d279 1 a279 1 c += **src - 'A' + 10; d281 1 a281 1 **dst = (char)c; d284 1 a284 1 } d286 3 a288 2 static var_rc_t expand_grouped_hex(const char** src, char** dst, const char* end) { d291 5 a295 6 while (*src < end && **src != '}') { if ((rc = expand_simple_hex(src, dst, end)) != 0) return rc; ++(*src); } d297 1 a297 1 return VAR_INCOMPLETE_GROUPED_HEX; d300 1 a300 1 } d302 2 a303 2 static var_rc_t expand_hex(const char** src, char** dst, const char* end) { d305 12 a316 13 return VAR_INCOMPLETE_HEX; if (**src == '{') { ++(*src); return expand_grouped_hex(src, dst, end); } else return expand_simple_hex(src, dst, end); } var_rc_t var_unescape(const char* src, size_t len, char* dst, int unescape_all) { const char* end = src + len; d319 50 a368 57 while (src < end) { if (*src == '\\') { if (++src == end) return VAR_INCOMPLETE_NAMED_CHARACTER; switch (*src) { case '\\': if (!unescape_all) { *dst++ = '\\'; } *dst++ = '\\'; break; case 'n': *dst++ = '\n'; break; case 't': *dst++ = '\t'; break; case 'r': *dst++ = '\r'; break; case 'x': ++src; if ((rc = expand_hex(&src, &dst, end)) != 0) return rc; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (end - src >= 3 && isdigit(src[1]) && isdigit(src[2])) { if ((rc = expand_octal(&src, &dst, end)) != 0) return rc; break; } default: if (!unescape_all) { *dst++ = '\\'; } *dst++ = *src; } ++src; } else *dst++ = *src++; } d371 1 a371 1 } d375 17 a391 18 static int variable(const char*, const char*, const var_config_t*, const char[256], var_cb_t, void*, int, tokenbuf*); static int command(const char*, const char*, const var_config_t*, const char[256], var_cb_t, void*, int, tokenbuf*); static int text(const char* begin, const char* end, char varinit, char escape) { const char* p; for (p = begin; p != end && *p != varinit; ++p) { if (*p == escape) { if (p+1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } } d393 1 a393 1 } d395 5 a399 5 static int varname(const char* begin, const char* end, const char nameclass[256]) { const char* p; for (p = begin; p != end && nameclass[(int)*p]; ++p) ; d401 1 a401 1 } d403 4 a406 5 static int number(const char* begin, const char* end) { const char* p; for (p = begin; p != end && isdigit(*p); ++p) ; d408 13 d422 2 d425 13 a437 14 static int substext(const char* begin, const char* end, const var_config_t* config) { const char* p; for (p = begin; p != end && *p != config->varinit && *p != '/'; ++p) { if (*p == config->escape) { if (p+1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } } return p - begin; a438 14 static int exptext(const char* begin, const char* end, const var_config_t* config) { const char* p; for (p = begin; p != end && *p != config->varinit && *p != config->enddelim && *p != ':'; ++p) { if (*p == config->escape) { if (p+1 == end) return VAR_INCOMPLETE_QUOTED_PAIR; else ++p; } } d440 1 a440 1 } d443 8 a450 6 static int expression(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* result) { const char* p = begin; const char* data; d466 1 a466 1 return 0; d469 1 a469 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d474 24 a497 30 do { rc = varname(p, end, nameclass); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(&name, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } } d503 4 a506 5 if (name.begin == name.end) { rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } d511 5 a515 7 if (p == end || (*p != config->enddelim && *p != ':')) { rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } else ++p; d519 2 a520 1 rc = (*lookup)(lookup_context, name.begin, name.end - name.begin, &data, &len, &buffer_size); d522 54 a575 61 goto error_return; else if (rc == 0) { /* The variable is undefined. What we'll do now depends on the force_expand flag. */ if (force_expand) { rc = VAR_UNDEFINED_VARIABLE; goto error_return; } else { /* Initialize result to point back to the original text in the buffer. */ result->begin = begin-1; result->end = p; result->buffer_size = 0; failed = 1; } } else { /* The preliminary result is the contents of the variable. This may be modified by the commands that may follow. */ result->begin = data; result->end = data + len; result->buffer_size = buffer_size; } if (p[-1] == ':') { /* Parse and execute commands. */ free_tokenbuf(&tmp); --p; while (p != end && *p == ':') { ++p; if (!failed) rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result); else rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; p += rc; if (failed) result->end += rc; } if (p == end || *p != config->enddelim) { rc = VAR_INCOMPLETE_VARIABLE_SPEC; goto error_return; } ++p; if (failed) ++result->end; } d590 1 a590 1 } d592 7 a598 6 static int variable(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* result) { const char* p = begin; const char* data; d610 1 a610 1 return 0; d613 1 a613 1 return VAR_INCOMPLETE_VARIABLE_SPEC; d620 21 a640 26 return rc; else if (rc > 0) { rc2 = (*lookup)(lookup_context, p, rc, &data, &len, &buffer_size); if (rc2 < 0) return rc2; else if (rc2 == 0) { if (force_expand) return VAR_UNDEFINED_VARIABLE; else { result->begin = begin; result->end = begin + 1 + rc; result->buffer_size = 0; return 1 + rc; } } else { result->begin = data; result->end = data + len; result->buffer_size = buffer_size; return 1 + rc; } } d644 2 a645 1 rc = expression(p, end, config, nameclass, lookup, lookup_context, force_expand, result); d647 1 a647 1 ++rc; d649 1 a649 1 } d651 7 a657 5 static int exptext_or_variable(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* result) { const char* p = begin; d665 1 a665 1 return 0; d667 25 a691 29 do { rc = exptext(p, end, config); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(result, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { p += rc; if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } } d701 1 a701 1 } d703 7 a709 5 static int substext_or_variable(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* result) { const char* p = begin; d717 1 a717 1 return 0; d719 25 a743 29 do { rc = substext(p, end, config); if (rc < 0) goto error_return; else if (rc > 0) { if (!append_to_tokenbuf(result, p, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else p += rc; } rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp); if (rc < 0) goto error_return; else if (rc > 0) { p += rc; if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } } d753 1 a753 1 } d756 2 a757 2 static int expand_class_description(tokenbuf* src, tokenbuf* dst) { d759 17 a775 22 const char* p = src->begin; while(p != src->end) { if ((src->end - p) >= 3 && p[1] == '-') { if (*p > p[2]) return VAR_INCORRECT_TRANSPOSE_CLASS_SPEC; for (c = *p, d = p[2]; c <= d; ++c) { if (!append_to_tokenbuf(dst, (char*)&c, 1)) return VAR_OUT_OF_MEMORY; } p += 3; } else { if (!append_to_tokenbuf(dst, p, 1)) return VAR_OUT_OF_MEMORY; else ++p; } } d777 1 a777 1 } d779 3 a781 2 static int transpose(tokenbuf* data, tokenbuf* search, tokenbuf* replace) { d783 1 a783 1 const char* p; d791 1 a791 1 goto error_return; d793 1 a793 1 goto error_return; d795 27 a821 33 if (srcclass.begin == srcclass.end) { rc = VAR_EMPTY_TRANSPOSE_CLASS; goto error_return; } if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) { rc = VAR_TRANSPOSE_CLASSES_MISMATCH; goto error_return; } if (data->buffer_size == 0) { tokenbuf tmp; if (!assign_to_tokenbuf(&tmp, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } move_tokenbuf(&tmp, data); } for (p = data->begin; p != data->end; ++p) { for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) { if (*p == srcclass.begin[i]) { *((char*)p) = dstclass.begin[i]; break; } } } d833 1 a833 1 } d835 3 a837 2 static int cut_out_offset(tokenbuf* data, tokenbuf* number1, tokenbuf* number2, int isrange) { d839 1 a839 1 const char* p; d846 1 a846 1 return VAR_OFFSET_OUT_OF_BOUNDS; d848 1 a848 1 p = data->begin + num1; d852 19 a870 24 if (num2 == 0) { if (!assign_to_tokenbuf(&res, p, data->end - p)) return VAR_OUT_OF_MEMORY; } else /* OK, then use num2. */ { if (isrange) { if ((p + num2) > data->end) return VAR_RANGE_OUT_OF_BOUNDS; if (!assign_to_tokenbuf(&res, p, num2)) return VAR_OUT_OF_MEMORY; } else { if (num2 < num1) return VAR_OFFSET_LOGIC_ERROR; if ((data->begin + num2) > data->end) return VAR_RANGE_OUT_OF_BOUNDS; if (!assign_to_tokenbuf(&res, p, (data->begin + num2) - p)) return VAR_OUT_OF_MEMORY; } } d874 1 a874 1 } d876 4 a879 3 static int expand_regex_replace(const char* data, tokenbuf* orig, regmatch_t* pmatch, tokenbuf* expanded) { const char* p = orig->begin; d884 38 a921 50 while(p != orig->end) { if (*p == '\\') { if (orig->end - p <= 1) { free_tokenbuf(expanded); return VAR_INCOMPLETE_QUOTED_PAIR; } else ++p; if (*p == '\\') { if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } ++p; continue; } if (!isdigit(*p)) { free_tokenbuf(expanded); return VAR_UNKNOWN_QUOTED_PAIR_IN_REPLACE; } i = *p - '0'; ++p; if (pmatch[i].rm_so == -1) { free_tokenbuf(expanded); return VAR_SUBMATCH_OUT_OF_RANGE; } if (!append_to_tokenbuf(expanded, data + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } } else { if (!append_to_tokenbuf(expanded, p, 1)) { free_tokenbuf(expanded); return VAR_OUT_OF_MEMORY; } ++p; } } d924 1 a924 1 } d926 4 a929 3 static int search_and_replace(tokenbuf* data, tokenbuf* search, tokenbuf* replace, tokenbuf* flags) { const char* p; d936 1 a936 1 return VAR_EMPTY_SEARCH_STRING; d938 124 a1061 138 for (p = flags->begin; p != flags->end; ++p) { switch (tolower(*p)) { case 'i': case_insensitive = 1; break; case 'g': global = 1; break; case 't': no_regex = 1; break; default: return VAR_UNKNOWN_REPLACE_FLAG; } } if (no_regex) { tokenbuf tmp; init_tokenbuf(&tmp); for (p = data->begin; p != data->end; ) { if (case_insensitive) rc = strncasecmp(p, search->begin, search->end - search->begin); else rc = strncmp(p, search->begin, search->end - search->begin); if (rc != 0) { /* no match, copy character */ if (!append_to_tokenbuf(&tmp, p, 1)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } ++p; } else { append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin); p += search->end - search->begin; if (!global) { if (!append_to_tokenbuf(&tmp, p, data->end - p)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } break; } } } free_tokenbuf(data); move_tokenbuf(&tmp, data); } else { tokenbuf tmp; tokenbuf mydata; tokenbuf myreplace; regex_t preg; regmatch_t pmatch[10]; int regexec_flag; /* Copy the pattern and the data to our own buffer to make sure they're terminated with a null byte. */ if (!assign_to_tokenbuf(&tmp, search->begin, search->end - search->begin)) return VAR_OUT_OF_MEMORY; if (!assign_to_tokenbuf(&mydata, data->begin, data->end - data->begin)) { free_tokenbuf(&tmp); return VAR_OUT_OF_MEMORY; } /* Compile the pattern. */ rc = regcomp(&preg, tmp.begin, REG_EXTENDED | ((case_insensitive) ? REG_ICASE : 0)); free_tokenbuf(&tmp); if (rc != 0) { free_tokenbuf(&mydata); return VAR_INVALID_REGEX_IN_REPLACE; } /* Match the pattern and create the result string in the tmp buffer. */ for (p = mydata.begin; p != mydata.end; ) { if (p == mydata.begin || p[-1] == '\n') regexec_flag = 0; else regexec_flag = REG_NOTBOL; if (regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH) { append_to_tokenbuf(&tmp, p, mydata.end - p); break; } else { rc = expand_regex_replace(p, replace, pmatch, &myreplace); if (rc != VAR_OK) { regfree(&preg); free_tokenbuf(&tmp); free_tokenbuf(&mydata); return rc; } if (!append_to_tokenbuf(&tmp, p, pmatch[0].rm_so) || !append_to_tokenbuf(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { regfree(&preg); free_tokenbuf(&tmp); free_tokenbuf(&mydata); free_tokenbuf(&myreplace); return VAR_OUT_OF_MEMORY; } else { p += (pmatch[0].rm_eo > 0) ? pmatch[0].rm_eo : 1; free_tokenbuf(&myreplace); } if (!global) { append_to_tokenbuf(&tmp, p, mydata.end - p); break; } } } regfree(&preg); free_tokenbuf(data); move_tokenbuf(&tmp, data); free_tokenbuf(&mydata); } d1064 1 a1064 1 } d1066 3 a1068 2 static int padding(tokenbuf* data, tokenbuf* widthstr, tokenbuf* fill, char position) { d1074 1 a1074 1 return VAR_EMPTY_PADDING_FILL_STRING; d1078 95 a1172 106 if (position == 'l') { i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while(i > 0) { if (!append_to_tokenbuf(data, fill->begin, fill->end - fill->begin)) return VAR_OUT_OF_MEMORY; --i; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!append_to_tokenbuf(data, fill->begin, i)) return VAR_OUT_OF_MEMORY; } } else if (position == 'r') { i = width - (data->end - data->begin); if (i > 0) { i = i / (fill->end - fill->begin); while(i > 0) { if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = (width - (data->end - data->begin)) % (fill->end - fill->begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } free_tokenbuf(data); move_tokenbuf(&result, data); } } else if (position == 'c') { i = (width - (data->end - data->begin)) / 2; if (i > 0) { /* Create the prefix. */ i = i / (fill->end - fill->begin); while(i > 0) { if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Append the actual data string. */ if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Append the suffix. */ i = width - (result.end - result.begin); i = i / (fill->end - fill->begin); while(i > 0) { if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } --i; } i = width - (result.end - result.begin); if (!append_to_tokenbuf(&result, fill->begin, i)) { free_tokenbuf(&result); return VAR_OUT_OF_MEMORY; } /* Move string from temporary buffer to data buffer. */ free_tokenbuf(data); move_tokenbuf(&result, data); } } d1175 1 a1175 1 } d1177 6 a1182 5 static int command(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* data) { const char* p = begin; d1197 1 a1197 1 return 0; d1199 291 a1489 341 switch (tolower(*p)) { case 'l': /* Turn data to lowercase. */ if (data->begin) { char* ptr; /* If the buffer does not life in an allocated buffer, we have to copy it before modifying the contents. */ if (data->buffer_size == 0) { if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char*)data->begin; ptr != data->end; ++ptr) *ptr = tolower(*ptr); } ++p; break; case 'u': /* Turn data to uppercase. */ if (data->begin) { char* ptr; if (data->buffer_size == 0) { if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } for (ptr = (char*)data->begin; ptr != data->end; ++ptr) *ptr = toupper(*ptr); } ++p; break; case 'o': /* Cut out substrings. */ ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_START_OFFSET; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p == ',') { isrange = 0; ++p; } else if (*p == '-') { isrange = 1; ++p; } else { rc = VAR_INVALID_OFFSET_DELIMITER; goto error_return; } rc = number(p, end); number2.begin = p; number2.end = p + rc; number2.buffer_size = 0; p += rc; if (data->begin) { rc = cut_out_offset(data, &number1, &number2, isrange); if (rc < 0) goto error_return; } break; case '#': /* Substitute length of the string. */ if (data->begin) { char buf[1024]; sprintf(buf, "%d", data->end - data->begin); free_tokenbuf(data); if (!assign_to_tokenbuf(data, buf, strlen(buf))) { rc = VAR_OUT_OF_MEMORY; goto error_return; } } ++p; break; case '-': /* Substitute parameter if data is empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL && data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } break; case '*': /* Return "" if data is not empty, parameter otherwise. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin == data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } else { free_tokenbuf(data); data->begin = data->end = ""; data->buffer_size = 0; } } break; case '+': /* Substitute parameter if data is not empty. */ ++p; rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmptokbuf); if (rc < 0) goto error_return; else if (rc == 0) { rc = VAR_MISSING_PARAMETER_IN_COMMAND; goto error_return; } else p += rc; if (data->begin != NULL) { if (data->begin != data->end) { free_tokenbuf(data); move_tokenbuf(&tmptokbuf, data); } } break; case 's': /* Search and replace. */ ++p; if (*p != '/') return VAR_MALFORMATTED_REPLACE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_REPLACE; goto error_return; } else ++p; rc = exptext(p, end, config); if (rc < 0) goto error_return; else { flags.begin = p; flags.end = p + rc; flags.buffer_size = 0; p += rc; } if (data->begin) { rc = search_and_replace(data, &search, &replace, &flags); if (rc < 0) goto error_return; } break; case 'y': /* Transpose characters from class A to class B. */ ++p; if (*p != '/') return VAR_MALFORMATTED_TRANSPOSE; else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &search); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_TRANSPOSE; goto error_return; } else ++p; if (data->begin) { rc = transpose(data, &search, &replace); if (rc < 0) goto error_return; } break; case 'p': /* Padding. */ ++p; if (*p != '/') return VAR_MALFORMATTED_PADDING; else ++p; rc = number(p, end); if (rc == 0) { rc = VAR_MISSING_PADDING_WIDTH; goto error_return; } else { number1.begin = p; number1.end = p + rc; number1.buffer_size = 0; p += rc; } if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &replace); if (rc < 0) goto error_return; else p += rc; if (*p != '/') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (*p != 'l' && *p != 'c' && *p != 'r') { rc = VAR_MALFORMATTED_PADDING; goto error_return; } else ++p; if (data->begin) { rc = padding(data, &number1, &replace, p[-1]); if (rc < 0) goto error_return; } break; default: return VAR_UNKNOWN_COMMAND_CHAR; } d1510 1 a1510 1 } d1512 6 a1517 4 static var_rc_t input(const char* begin, const char* end, const var_config_t* config, const char nameclass[256], var_cb_t lookup, void* lookup_context, int force_expand, tokenbuf* output) { d1523 23 a1545 29 do { rc = text(begin, end, config->varinit, config->escape); if (rc > 0) { if (!append_to_tokenbuf(output, begin, rc)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } begin += rc; } else if (rc < 0) goto error_return; rc = variable(begin, end, config, nameclass, lookup, lookup_context, force_expand, &result); if (rc > 0) { if (!append_to_tokenbuf(output, result.begin, result.end - result.begin)) { rc = VAR_OUT_OF_MEMORY; goto error_return; } else begin += rc; } else if (rc < 0) goto error_return; } d1548 4 a1551 5 if (begin != end) { rc = VAR_INPUT_ISNT_TEXT_NOR_VARIABLE; goto error_return; } d1558 1 a1558 1 } d1560 5 a1564 5 var_rc_t var_expand(const char* input_buf, size_t input_len, char** result, size_t* result_len, var_cb_t lookup, void* lookup_context, const var_config_t* config, int force_expand) { d1572 1 a1572 1 config = &var_config_default; d1575 1 a1575 1 return rc; d1580 5 a1584 5 if (nameclass[(int)config->varinit] || nameclass[(int)config->startdelim] || nameclass[(int)config->enddelim] || nameclass[(int)config->escape]) return VAR_INVALID_CONFIGURATION; d1591 6 a1596 7 lookup, lookup_context, force_expand, &output); if (rc != VAR_OK) { free_tokenbuf(&output); return rc; } *result = (char*)output.begin; d1600 1 a1600 1 } @ 1.6 log @Removed all debug output except for that in search_and_replace(). @ text @d343 7 d935 61 a1006 3 printf("Search '%s' in '%s' and replace it with '%s'.\n", search->begin, data->begin, replace->begin); a1012 1 printf("case_insensitive = 1;\n"); a1015 1 printf("global = 1;\n"); a1018 1 printf("no_regex = 1;\n"); d1068 1 d1070 1 a1070 1 regmatch_t pmatch[33]; a1085 3 printf("data is.................: '%s'\n", mydata.begin); printf("regex search pattern is.: '%s'\n", tmp.begin); printf("regex replace pattern is: '%s'\n", replace->begin); a1092 1 printf("Subexpression in pattern: '%d'\n", preg.re_nsub); a1104 1 printf("No match; appending remainder ('%s') to output string.\n", p); d1110 8 d1119 1 a1119 1 !append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin)) d1124 1 d1128 4 a1131 1 p += pmatch[0].rm_eo; @ 1.5 log @Changed the semantics of var_unescape(): It will only recognize a named character as being an octal if there are three digits following the backslash. Anything else will be interpreted as an ordinary quoted character that's copied verbatimly. Added a test case to verify that behavior. @ text @a801 1 printf("Expand class.\n"); a812 1 printf("Copy verbatim.\n"); a836 3 printf("Transpose from '%s' to '%s'.\n", srcclass.begin, dstclass.begin); a1084 3 printf("Padding data '%s' to width '%d' by filling in '%s' to position '%c'.\n", data->begin, width, fill->begin, position); a1094 1 printf("Missing %d characters at the end of the data string.\n", i); a1095 1 printf("That's %d times the padding string.\n", i); a1102 1 printf("Plus a remainder of %d characters.\n", i); a1111 1 printf("Missing %d characters at the beginning of the data string.\n", i); a1112 1 printf("That's %d times the padding string.\n", i); a1122 1 printf("Plus a remainder of %d characters.\n", i); a1144 1 printf("Missing %d characters at the beginning of the data string.\n", i); a1145 1 printf("That's %d times the padding string.\n", i); a1155 1 printf("Plus a remainder of %d characters.\n", i); a1172 1 printf("Missing %d characters at the end of the data string.\n", i); a1173 1 printf("That's %d times the padding string.\n", i); a1183 1 printf("Plus a remainder of %d characters.\n", i); @ 1.4 log @Implemented the unescape_all-behavior and added modified the test program to make use of var_unescape(). Also added a test case, which verifies hex and octal characters are handled correctly. @ text @d367 1 a367 10 if (!unescape_all) { if (end - src >= 3 && isdigit(src[1]) && isdigit(src[2])) { if ((rc = expand_octal(&src, &dst, end)) != 0) return rc; break; } } else @ 1.3 log @Renamed expand_named_characters() to var_unescape while adding the flag telling the function whether to expand anything or only constructs it knows. @ text @d367 15 a381 3 if ((rc = expand_octal(&src, &dst, end)) != 0) return rc; break; d383 4 @ 1.2 log @- Fixed copyright header to show the right description. - Removed assertions. @ text @d330 1 a330 1 var_rc_t expand_named_characters(const char* src, size_t len, char* dst) @ 1.1 log @- Moved all routines into a single source file var.c. - Renamed varexp.h to var.h. - Removed odin-based build system. - Moved test cases into var_test.c. - Adapted build system. @ text @d27 1 a27 1 ** var.h: VAR library API a29 1 #include a187 3 assert(a <= b); assert(class != NULL); a198 3 assert(desc != NULL); assert(class != NULL); a334 3 assert(src != NULL); assert(dst != NULL); a1657 7 /* Assert everything is as we expect it. */ assert(input_buf != NULL); assert(result != NULL); assert(result_len != NULL); assert(lookup != NULL); @