2 ** OSSP var - Variable Expansion
3 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
4 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
5 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
7 ** This file is part of OSSP var, a variable expansion
8 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
10 ** Permission to use, copy, modify, and distribute this software for
11 ** any purpose with or without fee is hereby granted, provided that
12 ** the above copyright notice and this permission notice appear in all
15 ** For disclaimer see below.
18 * Adapted by Kern Sibbald to Bacula June 2003
21 Copyright (C) 2000-2004 Kern Sibbald and John Walker
23 This program is free software; you can redistribute it and/or
24 modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation; either version 2 of
26 the License, or (at your option) any later version.
28 This program is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31 General Public License for more details.
33 You should have received a copy of the GNU General Public
34 License along with this program; if not, write to the Free
35 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
41 #if defined(HAVE_PCREPOSIX)
42 # include <pcreposix.h>
43 #elif defined(HAVE_WIN32)
50 /* support for OSSP ex based exception throwing */
54 ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \
55 ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
57 #define VAR_RC(rv) (rv)
66 ** ==== INTERNAL DATA STRUCTURES ====
70 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
72 /* the external context structure */
75 char_class_t syntax_nameclass;
76 var_cb_value_t cb_value_fct;
78 var_cb_operation_t cb_operation_fct;
79 void *cb_operation_ctx;
82 /* the internal expansion context structure */
84 struct var_parse_st *lower;
90 typedef struct var_parse_st var_parse_t;
92 /* the default syntax configuration */
93 static const var_syntax_t var_syntax_default = {
97 '}', /* delim_close */
99 ']', /* index_close */
100 '#', /* index_mark */
101 "a-zA-Z0-9_" /* name_chars */
106 ** ==== FORMATTING FUNCTIONS ====
110 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
113 int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx,
114 const char *format, va_list ap)
116 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
117 char ibuf[((sizeof(int)*8)/3)+10];
127 while (*format != '\0') {
128 if (*format == '%') {
137 c = (char)va_arg(ap, int);
143 if ((cp = (char *)va_arg(ap, char *)) == NULL)
149 d = (int)va_arg(ap, int);
150 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
164 if ((format = strchr(cp, '%')) == NULL)
165 format = strchr(cp, '\0');
168 /* perform output operation */
170 if ((n = output(ctx, cp, n)) == -1)
177 /* output callback function context for var_mvsnprintf() */
181 } var_mvsnprintf_cb_t;
183 /* output callback function for var_mvsnprintf() */
187 const char *buffer, int bufsize)
189 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
191 if (bufsize > ctx->buflen)
193 memcpy(ctx->bufptr, buffer, bufsize);
194 ctx->bufptr += bufsize;
195 ctx->buflen -= bufsize;
199 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
202 char *buffer, int bufsize,
203 const char *format, va_list ap)
206 var_mvsnprintf_cb_t ctx;
210 if (buffer != NULL && bufsize == 0)
213 /* just determine output length */
214 n = var_mvxprintf(NULL, NULL, format, ap);
216 /* perform real output */
218 ctx.buflen = bufsize;
219 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
220 if (n != -1 && ctx.buflen == 0)
223 *(ctx.bufptr) = '\0';
230 ** ==== PARSE CONTEXT FUNCTIONS ====
236 var_parse_t *lower, var_parse_t *upper)
240 memcpy(upper, lower, sizeof(var_parse_t));
241 upper->lower = lower;
256 ** ==== TOKEN BUFFER FUNCTIONS ====
260 #define TOKENBUF_INITIAL_BUFSIZE 64
274 buf->buffer_size = 0;
282 if (buf->begin == NULL && buf->end == NULL)
291 if (buf->begin == buf->end)
298 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
302 buf->buffer_size = buffer_size;
308 tokenbuf_t *src, tokenbuf_t *dst)
310 dst->begin = src->begin;
312 dst->buffer_size = src->buffer_size;
319 tokenbuf_t *buf, const char *data, int len)
323 if ((p = (char *)malloc(len + 1)) == NULL)
325 memcpy(p, data, len);
328 buf->buffer_size = len + 1;
329 *((char *)(buf->end)) = EOS;
335 tokenbuf_t *output, const char *data, int len)
341 /* Is the tokenbuffer initialized at all? If not, allocate a
342 standard-sized buffer to begin with. */
343 if (output->begin == NULL) {
344 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
346 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
349 /* does the token contain text, but no buffer has been allocated yet? */
350 if (output->buffer_size == 0) {
351 /* check whether data borders to output. If, we can append
352 simly by increasing the end pointer. */
353 if (output->end == data) {
357 /* ok, so copy the contents of output into an allocated buffer
358 so that we can append that way. */
359 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
361 memcpy(tmp, output->begin, output->end - output->begin);
362 output->buffer_size = output->end - output->begin;
364 output->end = tmp + output->buffer_size;
365 output->buffer_size += len + 1;
368 /* does the token fit into the current buffer? If not, realloc a
369 larger buffer that fits. */
370 if ((output->buffer_size - (output->end - output->begin)) <= len) {
371 new_size = output->buffer_size;
374 } while ((new_size - (output->end - output->begin)) <= len);
375 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
377 output->end = new_buffer + (output->end - output->begin);
378 output->begin = new_buffer;
379 output->buffer_size = new_size;
382 /* append the data at the end of the current buffer. */
384 memcpy((char *)output->end, data, len);
386 *((char *)output->end) = EOS;
392 tokenbuf_t *output, tokenbuf_t *input)
394 return tokenbuf_append(output, input->begin, input->end - input->begin);
401 if (buf->begin != NULL && buf->buffer_size > 0)
402 free((char *)buf->begin);
403 buf->begin = buf->end = NULL;
404 buf->buffer_size = 0;
410 ** ==== CHARACTER CLASS EXPANSION ====
415 expand_range(char a, char b, char_class_t chrclass)
418 chrclass[(int)a] = 1;
424 expand_character_class(const char *desc, char_class_t chrclass)
428 /* clear the class array. */
429 for (i = 0; i < 256; ++i)
432 /* walk through class description and set appropriate entries in array */
433 while (*desc != EOS) {
434 if (desc[1] == '-' && desc[2] != EOS) {
435 if (desc[0] > desc[2])
436 return VAR_ERR_INCORRECT_CLASS_SPEC;
437 expand_range(desc[0], desc[2], chrclass);
440 chrclass[(int) *desc] = 1;
449 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
457 if (c >= '0' && c <= '7')
465 const char **src, char **dst, const char *end)
470 return VAR_ERR_INCOMPLETE_OCTAL;
471 if ( !expand_isoct(**src)
472 || !expand_isoct((*src)[1])
473 || !expand_isoct((*src)[2]))
474 return VAR_ERR_INVALID_OCTAL;
478 return VAR_ERR_OCTAL_TOO_LARGE;
497 if ((c >= '0' && c <= '9') ||
498 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
506 const char **src, char **dst, const char *end)
511 return VAR_ERR_INCOMPLETE_HEX;
512 if ( !expand_ishex(**src)
513 || !expand_ishex((*src)[1]))
514 return VAR_ERR_INVALID_HEX;
516 if (**src >= '0' && **src <= '9')
518 else if (**src >= 'a' && **src <= 'f')
519 c = **src - 'a' + 10;
520 else if (**src >= 'A' && **src <= 'F')
521 c = **src - 'A' + 10;
526 if (**src >= '0' && **src <= '9')
528 else if (**src >= 'a' && **src <= 'f')
529 c += **src - 'a' + 10;
530 else if (**src >= 'A' && **src <= 'F')
531 c += **src - 'A' + 10;
540 const char **src, char **dst, const char *end)
544 while (*src < end && **src != '}') {
545 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
550 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
557 const char **src, char **dst, const char *end)
560 return VAR_ERR_INCOMPLETE_HEX;
563 return expand_grouped_hex(src, dst, end);
565 return expand_simple_hex(src, dst, end);
570 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
574 /* forward declarations */
575 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
576 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
577 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
579 /* parse pattern text */
582 var_t *var, var_parse_t *ctx,
583 const char *begin, const char *end)
587 /* parse until '/' */
588 for (p = begin; p != end && *p != '/'; p++) {
589 if (*p == var->syntax.escape) {
591 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
598 /* parse substitution text */
601 var_t *var, var_parse_t *ctx,
602 const char *begin, const char *end)
606 /* parse until delim_init or '/' */
607 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
608 if (*p == var->syntax.escape) {
610 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
617 /* parse expression text */
620 var_t *var, var_parse_t *ctx,
621 const char *begin, const char *end)
625 /* parse until delim_init or delim_close or ':' */
626 for (p = begin; p != end
627 && *p != var->syntax.delim_init
628 && *p != var->syntax.delim_close
630 if (*p == var->syntax.escape) {
632 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
639 /* parse opertion argument text */
642 var_t *var, var_parse_t *ctx,
643 const char *begin, const char *end)
647 /* parse until delim_init or ')' */
648 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
649 if (*p == var->syntax.escape) {
651 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
659 parse_opargtext_or_variable(
660 var_t *var, var_parse_t *ctx,
661 const char *begin, const char *end,
668 tokenbuf_init(result);
674 rc = parse_opargtext(var, ctx, p, end);
678 if (!tokenbuf_append(result, p, rc)) {
679 rc = VAR_ERR_OUT_OF_MEMORY;
684 rc = parse_variable(var, ctx, p, end, &tmp);
689 if (!tokenbuf_merge(result, &tmp)) {
690 rc = VAR_ERR_OUT_OF_MEMORY;
694 tokenbuf_free(&tmp); /* KES 11/9/2003 */
701 tokenbuf_free(result);
705 /* parse expression or variable */
707 parse_exptext_or_variable(
708 var_t *var, var_parse_t *ctx,
709 const char *begin, const char *end,
712 const char *p = begin;
716 tokenbuf_init(result);
721 /* try to parse expression text */
722 rc = parse_exptext(var, ctx, p, end);
726 if (!tokenbuf_append(result, p, rc)) {
727 rc = VAR_ERR_OUT_OF_MEMORY;
733 /* try to parse variable construct */
734 rc = parse_variable(var, ctx, p, end, &tmp);
739 if (!tokenbuf_merge(result, &tmp)) {
740 rc = VAR_ERR_OUT_OF_MEMORY;
744 tokenbuf_free(&tmp); /* KES 11/9/2003 */
752 tokenbuf_free(result);
756 /* parse substitution text or variable */
758 parse_substext_or_variable(
759 var_t *var, var_parse_t *ctx,
760 const char *begin, const char *end,
763 const char *p = begin;
767 tokenbuf_init(result);
772 /* try to parse substitution text */
773 rc = parse_substext(var, ctx, p, end);
777 if (!tokenbuf_append(result, p, rc)) {
778 rc = VAR_ERR_OUT_OF_MEMORY;
784 /* try to parse substitution text */
785 rc = parse_variable(var, ctx, p, end, &tmp);
790 if (!tokenbuf_merge(result, &tmp)) {
791 rc = VAR_ERR_OUT_OF_MEMORY;
795 tokenbuf_free(&tmp); /* KES 11/9/2003 */
803 tokenbuf_free(result);
807 /* parse class description */
809 parse_class_description(
810 var_t *var, var_parse_t *ctx,
811 tokenbuf_t *src, tokenbuf_t *dst)
817 while (p != src->end) {
818 if ((src->end - p) >= 3 && p[1] == '-') {
820 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
821 for (c = *p, d = p[2]; c <= d; ++c) {
822 if (!tokenbuf_append(dst, (char *)&c, 1))
823 return VAR_ERR_OUT_OF_MEMORY;
827 if (!tokenbuf_append(dst, p, 1))
828 return VAR_ERR_OUT_OF_MEMORY;
835 /* parse regex replace part */
838 var_t *var, var_parse_t *ctx,
842 tokenbuf_t *expanded)
848 tokenbuf_init(expanded);
850 while (p != orig->end) {
852 if (orig->end - p <= 1) {
853 tokenbuf_free(expanded);
854 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
858 if (!tokenbuf_append(expanded, p, 1)) {
859 tokenbuf_free(expanded);
860 return VAR_ERR_OUT_OF_MEMORY;
865 if (!isdigit((int)*p)) {
866 tokenbuf_free(expanded);
867 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
871 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
872 tokenbuf_free(expanded);
873 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
875 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
876 pmatch[i].rm_eo - pmatch[i].rm_so)) {
877 tokenbuf_free(expanded);
878 return VAR_ERR_OUT_OF_MEMORY;
881 if (!tokenbuf_append(expanded, p, 1)) {
882 tokenbuf_free(expanded);
883 return VAR_ERR_OUT_OF_MEMORY;
892 /* operation: transpose */
895 var_t *var, var_parse_t *ctx,
900 tokenbuf_t srcclass, dstclass;
905 tokenbuf_init(&srcclass);
906 tokenbuf_init(&dstclass);
907 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
909 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
911 if (srcclass.begin == srcclass.end) {
912 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
915 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
916 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
919 if (data->buffer_size == 0) {
921 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
922 rc = VAR_ERR_OUT_OF_MEMORY;
925 tokenbuf_move(&tmp, data);
927 for (p = data->begin; p != data->end; ++p) {
928 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
929 if (*p == srcclass.begin[i]) {
930 *((char *)p) = dstclass.begin[i];
935 tokenbuf_free(&srcclass);
936 tokenbuf_free(&dstclass);
940 tokenbuf_free(search);
941 tokenbuf_free(replace);
942 tokenbuf_free(&srcclass);
943 tokenbuf_free(&dstclass);
947 /* operation: search & replace */
949 op_search_and_replace(
950 var_t *var, var_parse_t *ctx,
958 int case_insensitive = 0;
964 if (search->begin == search->end)
965 return VAR_ERR_EMPTY_SEARCH_STRING;
967 for (p = flags->begin; p != flags->end; p++) {
968 switch (tolower(*p)) {
973 case_insensitive = 1;
982 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
987 /* plain text pattern based operation */
989 for (p = data->begin; p != data->end;) {
990 if (case_insensitive)
991 rc = strncasecmp(p, search->begin, search->end - search->begin);
993 rc = strncmp(p, search->begin, search->end - search->begin);
995 /* not matched, copy character */
996 if (!tokenbuf_append(&tmp, p, 1)) {
998 return VAR_ERR_OUT_OF_MEMORY;
1002 /* matched, copy replacement string */
1003 tokenbuf_merge(&tmp, replace);
1004 p += (search->end - search->begin);
1006 /* append remaining text */
1007 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1008 tokenbuf_free(&tmp);
1009 return VAR_ERR_OUT_OF_MEMORY;
1015 tokenbuf_free(data);
1016 tokenbuf_move(&tmp, data);
1018 /* regular expression pattern based operation */
1020 tokenbuf_t myreplace;
1022 regmatch_t pmatch[10];
1025 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1026 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1027 return VAR_ERR_OUT_OF_MEMORY;
1028 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1029 tokenbuf_free(&tmp);
1030 return VAR_ERR_OUT_OF_MEMORY;
1033 /* compile the pattern. */
1034 rc = regcomp(&preg, tmp.begin,
1036 | (multiline ? REG_NEWLINE : 0)
1037 | (case_insensitive ? REG_ICASE : 0)));
1038 tokenbuf_free(&tmp);
1040 tokenbuf_free(&mydata);
1041 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1044 /* match the pattern and create the result string in the tmp buffer */
1045 tokenbuf_append(&tmp, "", 0);
1046 for (p = mydata.begin; p < mydata.end; ) {
1047 if (p == mydata.begin || p[-1] == '\n')
1050 regexec_flag = REG_NOTBOL;
1051 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1053 /* no (more) matching */
1054 tokenbuf_append(&tmp, p, mydata.end - p);
1058 && (p + pmatch[0].rm_so) == mydata.end
1059 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1060 /* special case: found empty pattern (usually /^/ or /$/ only)
1061 in multi-line at end of data (after the last newline) */
1062 tokenbuf_append(&tmp, p, mydata.end - p);
1066 /* append prolog string */
1067 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1069 tokenbuf_free(&tmp);
1070 tokenbuf_free(&mydata);
1071 return VAR_ERR_OUT_OF_MEMORY;
1073 /* create replace string */
1074 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1077 tokenbuf_free(&tmp);
1078 tokenbuf_free(&mydata);
1081 /* append replace string */
1082 if (!tokenbuf_merge(&tmp, &myreplace)) {
1084 tokenbuf_free(&tmp);
1085 tokenbuf_free(&mydata);
1086 tokenbuf_free(&myreplace);
1087 return VAR_ERR_OUT_OF_MEMORY;
1089 tokenbuf_free(&myreplace);
1090 /* skip now processed data */
1091 p += pmatch[0].rm_eo;
1092 /* if pattern matched an empty part (think about
1093 anchor-only regular expressions like /^/ or /$/) we
1094 skip the next character to make sure we do not enter
1095 an infinitive loop in matching */
1096 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1097 if (p >= mydata.end)
1099 if (!tokenbuf_append(&tmp, p, 1)) {
1101 tokenbuf_free(&tmp);
1102 tokenbuf_free(&mydata);
1103 return VAR_ERR_OUT_OF_MEMORY;
1107 /* append prolog string and stop processing if we
1108 do not perform the search & replace globally */
1110 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1112 tokenbuf_free(&tmp);
1113 tokenbuf_free(&mydata);
1114 return VAR_ERR_OUT_OF_MEMORY;
1121 tokenbuf_free(data);
1122 tokenbuf_move(&tmp, data);
1123 tokenbuf_free(&mydata);
1129 /* operation: offset substring */
1132 var_t *var, var_parse_t *ctx,
1141 /* determine begin of result string */
1142 if ((data->end - data->begin) < num1)
1143 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1144 p = data->begin + num1;
1146 /* if num2 is zero, we copy the rest from there. */
1148 if (!tokenbuf_assign(&res, p, data->end - p))
1149 return VAR_ERR_OUT_OF_MEMORY;
1151 /* ok, then use num2. */
1153 if ((p + num2) > data->end)
1154 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1155 if (!tokenbuf_assign(&res, p, num2))
1156 return VAR_ERR_OUT_OF_MEMORY;
1159 return VAR_ERR_OFFSET_LOGIC;
1160 if ((data->begin + num2) > data->end)
1161 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1162 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1163 return VAR_ERR_OUT_OF_MEMORY;
1166 tokenbuf_free(data);
1167 tokenbuf_move(&res, data);
1171 /* operation: padding */
1174 var_t *var, var_parse_t *ctx,
1183 if (fill->begin == fill->end)
1184 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1185 tokenbuf_init(&result);
1186 if (position == 'l') {
1188 i = width - (data->end - data->begin);
1190 i = i / (fill->end - fill->begin);
1192 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1193 return VAR_ERR_OUT_OF_MEMORY;
1196 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1197 if (!tokenbuf_append(data, fill->begin, i))
1198 return VAR_ERR_OUT_OF_MEMORY;
1200 } else if (position == 'r') {
1202 i = width - (data->end - data->begin);
1204 i = i / (fill->end - fill->begin);
1206 if (!tokenbuf_merge(&result, fill)) {
1207 tokenbuf_free(&result);
1208 return VAR_ERR_OUT_OF_MEMORY;
1212 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1213 if (!tokenbuf_append(&result, fill->begin, i)) {
1214 tokenbuf_free(&result);
1215 return VAR_ERR_OUT_OF_MEMORY;
1217 if (!tokenbuf_merge(&result, data)) {
1218 tokenbuf_free(&result);
1219 return VAR_ERR_OUT_OF_MEMORY;
1221 /* move string from temporary buffer to data buffer */
1222 tokenbuf_free(data);
1223 tokenbuf_move(&result, data);
1225 } else if (position == 'c') {
1226 /* centered padding */
1227 i = (width - (data->end - data->begin)) / 2;
1229 /* create the prefix */
1230 i = i / (fill->end - fill->begin);
1232 if (!tokenbuf_merge(&result, fill)) {
1233 tokenbuf_free(&result);
1234 return VAR_ERR_OUT_OF_MEMORY;
1238 i = ((width - (data->end - data->begin)) / 2)
1239 % (fill->end - fill->begin);
1240 if (!tokenbuf_append(&result, fill->begin, i)) {
1241 tokenbuf_free(&result);
1242 return VAR_ERR_OUT_OF_MEMORY;
1244 /* append the actual data string */
1245 if (!tokenbuf_merge(&result, data)) {
1246 tokenbuf_free(&result);
1247 return VAR_ERR_OUT_OF_MEMORY;
1249 /* append the suffix */
1250 i = width - (result.end - result.begin);
1251 i = i / (fill->end - fill->begin);
1253 if (!tokenbuf_merge(&result, fill)) {
1254 tokenbuf_free(&result);
1255 return VAR_ERR_OUT_OF_MEMORY;
1259 i = width - (result.end - result.begin);
1260 if (!tokenbuf_append(&result, fill->begin, i)) {
1261 tokenbuf_free(&result);
1262 return VAR_ERR_OUT_OF_MEMORY;
1264 /* move string from temporary buffer to data buffer */
1265 tokenbuf_free(data);
1266 tokenbuf_move(&result, data);
1272 /* parse an integer number ("123") */
1275 var_t *var, var_parse_t *ctx,
1276 const char *begin, const char *end,
1284 while (isdigit(*p) && p != end) {
1294 /* parse an operation (":x...") */
1297 var_t *var, var_parse_t *ctx,
1298 const char *begin, const char *end,
1302 tokenbuf_t tmptokbuf;
1303 tokenbuf_t search, replace, flags;
1304 tokenbuf_t number1, number2;
1310 /* initialization */
1311 tokenbuf_init(&tmptokbuf);
1312 tokenbuf_init(&search);
1313 tokenbuf_init(&replace);
1314 tokenbuf_init(&flags);
1315 tokenbuf_init(&number1);
1316 tokenbuf_init(&number2);
1321 /* dispatch through the first operation character */
1322 switch (tolower(*p)) {
1324 /* turn value to lowercase. */
1325 if (data->begin != NULL) {
1326 /* if the buffer does not live in an allocated buffer,
1327 we have to copy it before modifying the contents. */
1328 if (data->buffer_size == 0) {
1329 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1330 rc = VAR_ERR_OUT_OF_MEMORY;
1335 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1336 *ptr = (char)tolower((int)(*ptr));
1342 /* turn value to uppercase. */
1343 if (data->begin != NULL) {
1344 /* if the buffer does not live in an allocated buffer,
1345 we have to copy it before modifying the contents. */
1346 if (data->buffer_size == 0) {
1347 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1348 rc = VAR_ERR_OUT_OF_MEMORY;
1353 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1354 *ptr = (char)toupper((int)(*ptr));
1360 /* cut out substring of value. */
1362 rc = parse_integer(var, ctx, p, end, &num1);
1364 rc = VAR_ERR_MISSING_START_OFFSET;
1373 } else if (*p == '-') {
1377 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1380 rc = parse_integer(var, ctx, p, end, &num2);
1382 if (data->begin != NULL) {
1383 rc = op_offset(var, ctx, data, num1, num2, isrange);
1390 /* determine length of the value */
1391 if (data->begin != NULL) {
1392 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1393 sprintf(buf, "%d", (int)(data->end - data->begin));
1394 tokenbuf_free(data);
1395 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1396 rc = VAR_ERR_OUT_OF_MEMORY;
1404 /* substitute parameter if data is empty */
1406 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1410 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1414 if (tokenbuf_isundef(data))
1415 tokenbuf_move(&tmptokbuf, data);
1416 else if (tokenbuf_isempty(data)) {
1417 tokenbuf_free(data);
1418 tokenbuf_move(&tmptokbuf, data);
1423 /* substitute empty string if data is not empty, parameter otherwise. */
1425 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1429 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1433 if (data->begin != NULL) {
1434 if (data->begin == data->end) {
1435 tokenbuf_free(data);
1436 tokenbuf_move(&tmptokbuf, data);
1438 tokenbuf_free(data);
1439 data->begin = data->end = "";
1440 data->buffer_size = 0;
1446 /* substitute parameter if data is not empty. */
1448 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1452 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1456 if (data->begin != NULL && data->begin != data->end) {
1457 tokenbuf_free(data);
1458 tokenbuf_move(&tmptokbuf, data);
1463 /* search and replace. */
1466 return VAR_ERR_MALFORMATTED_REPLACE;
1468 rc = parse_pattern(var, ctx, p, end);
1471 tokenbuf_set(&search, p, p + rc, 0);
1474 rc = VAR_ERR_MALFORMATTED_REPLACE;
1478 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1483 rc = VAR_ERR_MALFORMATTED_REPLACE;
1487 rc = parse_exptext(var, ctx, p, end);
1490 tokenbuf_set(&flags, p, p + rc, 0);
1492 if (data->begin != NULL) {
1493 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1500 /* transpose characters from class A to class B. */
1503 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1505 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1510 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1514 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1519 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1524 rc = op_transpose(var, ctx, data, &search, &replace);
1534 return VAR_ERR_MALFORMATTED_PADDING;
1536 rc = parse_integer(var, ctx, p, end, &num1);
1538 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1543 rc = VAR_ERR_MALFORMATTED_PADDING;
1547 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1552 rc = VAR_ERR_MALFORMATTED_PADDING;
1556 if (*p != 'l' && *p != 'c' && *p != 'r') {
1557 rc = VAR_ERR_MALFORMATTED_PADDING;
1562 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1569 /* operation callback function */
1572 const char *arg_ptr;
1574 const char *val_ptr;
1576 const char *out_ptr;
1582 rc = parse_name(var, ctx, p, end);
1590 tokenbuf_init(&args);
1591 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1595 arg_ptr = args.begin;
1596 arg_len = args.end - args.begin;
1598 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1607 val_ptr = data->begin;
1608 val_len = data->end - data->begin;
1610 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1611 /* call operation callback function */
1612 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1616 &out_ptr, &out_len, &out_size);
1618 if (arg_ptr != NULL)
1619 free((void *)arg_ptr);
1622 tokenbuf_free(data);
1623 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1625 if (arg_ptr != NULL)
1626 free((void *)arg_ptr);
1630 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1633 /* return successfully */
1634 tokenbuf_free(&tmptokbuf);
1635 tokenbuf_free(&search);
1636 tokenbuf_free(&replace);
1637 tokenbuf_free(&flags);
1638 tokenbuf_free(&number1);
1639 tokenbuf_free(&number2);
1642 /* return with an error */
1644 tokenbuf_free(data);
1645 tokenbuf_free(&tmptokbuf);
1646 tokenbuf_free(&search);
1647 tokenbuf_free(&replace);
1648 tokenbuf_free(&flags);
1649 tokenbuf_free(&number1);
1650 tokenbuf_free(&number2);
1654 /* parse numerical expression operand */
1656 parse_numexp_operand(
1657 var_t *var, var_parse_t *ctx,
1658 const char *begin, const char *end,
1659 int *result, int *failed)
1666 /* initialization */
1668 tokenbuf_init(&tmp);
1670 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1672 /* parse opening numerical expression */
1674 /* parse inner numerical expression */
1675 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1680 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1681 /* parse closing parenthesis */
1683 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1686 /* parse contained variable */
1687 else if (*p == var->syntax.delim_init) {
1688 /* parse variable with forced expansion */
1689 ctx = var_parse_push(ctx, &myctx);
1690 ctx->force_expand = 1;
1691 rc = parse_variable(var, ctx, p, end, &tmp);
1692 ctx = var_parse_pop(ctx);
1694 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1696 /* parse variable without forced expansion */
1697 ctx = var_parse_push(ctx, &myctx);
1698 ctx->force_expand = 0;
1699 rc = parse_variable(var, ctx, p, end, &tmp);
1700 ctx = var_parse_pop(ctx);
1705 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1706 } else if (rc < 0) {
1710 /* parse remaining numerical expression */
1711 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1712 tokenbuf_free(&tmp);
1717 /* parse relative index mark ("#") */
1718 else if ( var->syntax.index_mark != EOS
1719 && *p == var->syntax.index_mark) {
1721 *result = ctx->index_this;
1722 if (ctx->rel_lookup_flag)
1723 ctx->rel_lookup_cnt++;
1725 /* parse plain integer number */
1726 else if (isdigit(*p)) {
1727 rc = parse_integer(var, ctx, p, end, result);
1730 /* parse signed positive integer number */
1731 else if (*p == '+') {
1732 if ((end - p) > 1 && isdigit(p[1])) {
1734 rc = parse_integer(var, ctx, p, end, result);
1738 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1740 /* parse signed negative integer number */
1741 else if (*p == '-') {
1742 if (end - p > 1 && isdigit(p[1])) {
1744 rc = parse_integer(var, ctx, p, end, result);
1745 *result = -(*result);
1749 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1751 /* else we failed to parse anything reasonable */
1753 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1758 /* parse numerical expression ("x+y") */
1761 var_t *var, var_parse_t *ctx,
1762 const char *begin, const char *end,
1763 int *result, int *failed)
1770 /* initialization */
1773 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1775 /* parse left numerical operand */
1776 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1781 /* parse numerical operator */
1783 if (*p == '+' || *p == '-') {
1785 /* recursively parse right operand (light binding) */
1786 rc = parse_numexp(var, ctx, p, end, &right, failed);
1791 *result = (*result + right);
1793 *result = (*result - right);
1795 else if (*p == '*' || *p == '/' || *p == '%') {
1797 /* recursively parse right operand (string binding) */
1798 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1803 *result = (*result * right);
1804 else if (op == '/') {
1809 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1812 *result = (*result / right);
1814 else if (op == '%') {
1819 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1822 *result = (*result % right);
1829 /* return amount of parsed input */
1833 /* parse variable name ("abc") */
1836 var_t *var, var_parse_t *ctx,
1837 const char *begin, const char *end)
1841 /* parse as long as name class characters are found */
1842 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1847 /* lookup a variable value through the callback function */
1850 var_t *var, var_parse_t *ctx,
1851 const char *var_ptr, int var_len, int var_inc, int var_idx,
1852 const char **val_ptr, int *val_len, int *val_size)
1857 /* pass through to original callback */
1858 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1859 var_ptr, var_len, var_inc, var_idx,
1860 val_ptr, val_len, val_size);
1862 /* convert undefined variable into empty variable if relative
1863 lookups are counted. This is the case inside an active loop
1864 construct if no limits are given. There the parse_input()
1865 has to proceed until all variables have undefined values.
1866 This trick here allows it to determine this case. */
1867 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1868 ctx->rel_lookup_cnt--;
1879 /* parse complex variable construct ("${name...}") */
1881 parse_variable_complex(
1882 var_t *var, var_parse_t *ctx,
1883 const char *begin, const char *end,
1888 int len, buffer_size;
1896 /* initializations */
1898 tokenbuf_init(&name);
1899 tokenbuf_init(&tmp);
1900 tokenbuf_init(result);
1902 /* parse open delimiter */
1903 if (p == end || *p != var->syntax.delim_open)
1907 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1909 /* parse name of variable to expand. The name may consist of an
1910 arbitrary number of variable name character and contained variable
1913 /* parse a variable name */
1914 rc = parse_name(var, ctx, p, end);
1918 if (!tokenbuf_append(&name, p, rc)) {
1919 rc = VAR_ERR_OUT_OF_MEMORY;
1925 /* parse an (embedded) variable */
1926 rc = parse_variable(var, ctx, p, end, &tmp);
1930 if (!tokenbuf_merge(&name, &tmp)) {
1931 rc = VAR_ERR_OUT_OF_MEMORY;
1936 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1939 /* we must have the complete expanded variable name now,
1940 so make sure we really do. */
1941 if (name.begin == name.end) {
1942 if (ctx->force_expand) {
1943 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1947 /* If no force_expand is requested, we have to back-off.
1948 We're not sure whether our approach here is 100% correct,
1949 because it _could_ have side-effects according to Peter
1950 Simons, but as far as we know and tried it, it is
1951 correct. But be warned -- RSE */
1952 tokenbuf_set(result, begin - 1, p, 0);
1957 /* parse an optional index specification */
1958 if ( var->syntax.index_open != EOS
1959 && *p == var->syntax.index_open) {
1961 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1965 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1970 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1973 if (*p != var->syntax.index_close) {
1974 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1980 /* parse end of variable construct or start of post-operations */
1981 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1982 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1985 inc = (*p == '+'); /* increment variable */
1988 /* lookup the variable value now */
1990 tokenbuf_set(result, begin - 1, p, 0);
1992 rc = lookup_value(var, ctx,
1993 name.begin, name.end-name.begin, inc, idx,
1994 &data, &len, &buffer_size);
1995 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1996 tokenbuf_init(result); /* delayed handling of undefined variable */
1997 } else if (rc < 0) {
2000 /* the preliminary result is the raw value of the variable.
2001 This may be modified by the operations that may follow. */
2002 tokenbuf_set(result, data, data + len, buffer_size);
2006 /* parse optional post-operations */
2009 tokenbuf_free(&tmp);
2010 tokenbuf_init(&tmp);
2012 while (p != end && *p == ':') {
2015 rc = parse_operation(var, ctx, p, end, result);
2017 rc = parse_operation(var, ctx, p, end, &tmp);
2024 if (p == end || *p != var->syntax.delim_close) {
2025 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2031 } else if (p[-1] == '+') {
2035 /* lazy handling of undefined variable */
2036 if (!failed && tokenbuf_isundef(result)) {
2037 if (ctx->force_expand) {
2038 rc = VAR_ERR_UNDEFINED_VARIABLE;
2041 tokenbuf_set(result, begin - 1, p, 0);
2045 /* return successfully */
2046 tokenbuf_free(&name);
2047 tokenbuf_free(&tmp);
2050 /* return with an error */
2052 tokenbuf_free(&name);
2053 tokenbuf_free(&tmp);
2054 tokenbuf_free(result);
2058 /* parse variable construct ("$name" or "${name...}") */
2061 var_t *var, var_parse_t *ctx,
2062 const char *begin, const char *end,
2067 int len, buffer_size;
2071 /* initialization */
2073 tokenbuf_init(result);
2075 /* parse init delimiter */
2076 if (p == end || *p != var->syntax.delim_init)
2080 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2082 /* parse a simple variable name.
2083 (if this fails, we're try to parse a complex variable construct) */
2084 rc = parse_name(var, ctx, p, end);
2088 inc = (p[rc] == '+');
2089 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2090 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2091 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2096 tokenbuf_set(result, data, data + len, buffer_size);
2100 /* parse a complex variable construct (else case) */
2101 rc = parse_variable_complex(var, ctx, p, end, result);
2107 /* parse loop construct limits ("[...]{b,s,e}") */
2110 var_t *var, var_parse_t *ctx,
2111 const char *begin, const char *end,
2112 int *start, int *step, int *stop, int *open_stop)
2118 /* initialization */
2121 /* we are happy if nothing is to left to parse */
2125 /* parse start delimiter */
2126 if (*p != var->syntax.delim_open)
2130 /* parse loop start value */
2132 rc = parse_numexp(var, ctx, p, end, start, &failed);
2133 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2134 *start = 0; /* use default */
2136 return (var_rc_t)rc;
2140 return VAR_ERR_UNDEFINED_VARIABLE;
2142 /* parse separator */
2144 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2147 /* parse loop step value */
2149 rc = parse_numexp(var, ctx, p, end, step, &failed);
2150 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2151 *step = 1; /* use default */
2153 return (var_rc_t)rc;
2157 return VAR_ERR_UNDEFINED_VARIABLE;
2159 /* parse separator */
2161 /* if not found, parse end delimiter */
2162 if (*p != var->syntax.delim_close)
2163 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2166 /* shift step value to stop value */
2170 /* determine whether loop end is open */
2175 return (var_rc_t)(p - begin);
2179 /* parse loop stop value */
2181 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2182 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2183 *stop = 0; /* use default */
2187 return (var_rc_t)rc;
2193 return VAR_ERR_UNDEFINED_VARIABLE;
2195 /* parse end delimiter */
2196 if (*p != var->syntax.delim_close)
2197 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2200 /* return amount of parsed input */
2201 return (var_rc_t)(p - begin);
2204 /* parse plain text */
2207 var_t *var, var_parse_t *ctx,
2208 const char *begin, const char *end)
2212 /* parse until delim_init (variable construct)
2213 or index_open (loop construct) is found */
2214 for (p = begin; p != end; p++) {
2215 if (*p == var->syntax.escape) {
2216 p++; /* skip next character */
2218 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2220 else if (*p == var->syntax.delim_init)
2222 else if ( var->syntax.index_open != EOS
2223 && ( *p == var->syntax.index_open
2224 || *p == var->syntax.index_close))
2230 /* expand input in general */
2233 var_t *var, var_parse_t *ctx,
2234 const char *begin, const char *end,
2235 tokenbuf_t *output, int recursion_level)
2240 int start, step, stop, open_stop;
2244 int loop_limit_length;
2247 /* initialization */
2251 /* try to parse a loop construct */
2253 && var->syntax.index_open != EOS
2254 && *p == var->syntax.index_open) {
2257 /* loop preparation */
2258 loop_limit_length = -1;
2259 rel_lookup_cnt = ctx->rel_lookup_cnt;
2267 /* iterate over loop construct, either as long as there is
2268 (still) nothing known about the limit, or there is an open
2269 (=unknown) limit stop and there are still defined variables
2270 or there is a stop limit known and it is still not reached */
2274 && ( loop_limit_length < 0
2275 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2280 /* remember current output end for restoring */
2281 output_backup = (output->end - output->begin);
2283 /* open temporary context for recursion */
2284 ctx = var_parse_push(ctx, &myctx);
2285 ctx->force_expand = 1;
2286 ctx->rel_lookup_flag = 1;
2287 ctx->index_this = i;
2289 /* recursive parse input through ourself */
2290 rc = parse_input(var, ctx, p, end,
2291 output, recursion_level+1);
2293 /* retrieve info and close temporary context */
2294 rel_lookup_cnt = ctx->rel_lookup_cnt;
2295 ctx = var_parse_pop(ctx);
2297 /* error handling */
2301 /* make sure the loop construct is closed */
2302 if (p[rc] != var->syntax.index_close) {
2303 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2307 /* try to parse loop construct limit specification */
2308 if (loop_limit_length < 0) {
2309 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2310 &start, &step, &stop, &open_stop);
2314 loop_limit_length = 0;
2316 loop_limit_length = rc2;
2317 /* restart loop from scratch */
2318 output->end = (output->begin + output_backup);
2324 /* if stop value is open, restore to the output end
2325 because the last iteration was just to determine the loop
2326 termination and its result has to be discarded */
2328 output->end = (output->begin + output_backup);
2330 /* skip parsed loop construct */
2333 p += loop_limit_length;
2338 /* try to parse plain text */
2339 rc = parse_text(var, ctx, p, end);
2341 if (!tokenbuf_append(output, p, rc)) {
2342 rc = VAR_ERR_OUT_OF_MEMORY;
2350 /* try to parse a variable construct */
2351 tokenbuf_init(&result);
2352 rc = parse_variable(var, ctx, p, end, &result);
2354 if (!tokenbuf_merge(output, &result)) {
2355 tokenbuf_free(&result);
2356 rc = VAR_ERR_OUT_OF_MEMORY;
2359 tokenbuf_free(&result);
2363 tokenbuf_free(&result);
2367 } while (p != end && rc > 0);
2369 /* We do not know whether this really could happen, but because we
2370 are paranoid, report an error at the outer most parsing level if
2371 there is still any input. Because this would mean that we are no
2372 longer able to parse the remaining input as a loop construct, a
2373 text or a variable construct. This would be very strange, but
2374 could perhaps happen in case of configuration errors!?... */
2375 if (recursion_level == 0 && p != end) {
2376 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2380 /* return amount of parsed text */
2381 return (var_rc_t)(p - begin);
2383 /* return with an error where as a special case the output begin is
2384 set to the input begin and the output end to the last input parsing
2387 tokenbuf_free(output);
2388 tokenbuf_set(output, begin, p, 0);
2389 return (var_rc_t)rc;
2394 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2398 /* create variable expansion context */
2406 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2407 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2408 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2409 memset(var, 0, sizeof(var));
2410 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2415 /* destroy variable expansion context */
2421 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2426 /* configure variable expansion context */
2437 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2440 case VAR_CONFIG_SYNTAX: {
2442 s = (var_syntax_t *)va_arg(ap, void *);
2444 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2445 var->syntax.escape = s->escape;
2446 var->syntax.delim_init = s->delim_init;
2447 var->syntax.delim_open = s->delim_open;
2448 var->syntax.delim_close = s->delim_close;
2449 var->syntax.index_open = s->index_open;
2450 var->syntax.index_close = s->index_close;
2451 var->syntax.index_mark = s->index_mark;
2452 var->syntax.name_chars = NULL; /* unused internally */
2453 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2455 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2456 || var->syntax_nameclass[(int)var->syntax.delim_open]
2457 || var->syntax_nameclass[(int)var->syntax.delim_close]
2458 || var->syntax_nameclass[(int)var->syntax.escape])
2459 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2462 case VAR_CONFIG_CB_VALUE: {
2465 fct = (var_cb_value_t)va_arg(ap, void *);
2466 ctx = (void *)va_arg(ap, void *);
2467 var->cb_value_fct = fct;
2468 var->cb_value_ctx = ctx;
2471 case VAR_CONFIG_CB_OPERATION: {
2472 var_cb_operation_t fct;
2474 fct = (var_cb_operation_t)va_arg(ap, void *);
2475 ctx = (void *)va_arg(ap, void *);
2476 var->cb_operation_fct = fct;
2477 var->cb_operation_ctx = ctx;
2481 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2487 /* perform unescape operation on a buffer */
2491 const char *src, int srclen,
2492 char *dst, int dstlen,
2498 if (var == NULL || src == NULL || dst == NULL)
2499 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2504 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2523 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2526 case '0': case '1': case '2': case '3': case '4':
2527 case '5': case '6': case '7': case '8': case '9':
2529 && isdigit((int)src[1])
2530 && isdigit((int)src[2])) {
2531 if ((rc = expand_octal(&src, &dst, end)) != 0)
2549 /* perform expand operation on a buffer */
2553 const char *src_ptr, int src_len,
2554 char **dst_ptr, int *dst_len,
2561 /* argument sanity checks */
2562 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2563 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2565 /* prepare internal expansion context */
2567 ctx.force_expand = force_expand;
2568 ctx.rel_lookup_flag = 0;
2569 ctx.rel_lookup_cnt = 0;
2572 /* start the parsing */
2573 tokenbuf_init(&output);
2574 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2576 /* post-processing */
2578 /* always EOS-terminate output for convinience reasons
2579 but do not count the EOS-terminator in the length */
2580 if (!tokenbuf_append(&output, "\0", 1)) {
2581 tokenbuf_free(&output);
2582 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2586 /* provide result */
2587 *dst_ptr = (char *)output.begin;
2588 if (dst_len != NULL)
2589 *dst_len = (output.end - output.begin);
2593 /* provide result */
2594 if (dst_len != NULL)
2595 *dst_len = (output.end - output.begin);
2601 /* format and expand a string */
2605 char **dst_ptr, int force_expand,
2606 const char *fmt, va_list ap)
2612 /* argument sanity checks */
2613 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2614 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2616 /* perform formatting */
2617 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2618 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2619 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2622 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2625 /* perform expansion */
2626 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2637 /* format and expand a string */
2641 char **dst_ptr, int force_expand,
2642 const char *fmt, ...)
2647 /* argument sanity checks */
2648 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2649 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2652 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2658 /* var_rc_t to string mapping table */
2659 static const char *var_errors[] = {
2660 _("everything ok"), /* VAR_OK = 0 */
2661 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2662 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2663 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2664 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2665 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2666 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2667 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2668 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2669 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2670 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2671 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2672 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2673 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2674 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2675 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2676 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2677 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2678 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2679 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2680 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2681 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2682 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2683 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2684 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2685 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2686 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2687 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2688 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2689 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2690 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2691 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2692 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2693 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2694 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2695 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2696 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2697 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2698 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2699 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2700 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2701 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2702 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2703 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2704 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2705 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2708 /* translate a return code into its corresponding descriptive text */
2709 const char *var_strerror(var_t *var, var_rc_t rc)
2712 rc = (var_rc_t)(0 - rc);
2713 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2714 str = _("unknown error");
2716 str = (char *)var_errors[rc];