2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 ** OSSP var - Variable Expansion
22 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
23 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
24 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
26 ** This file is part of OSSP var, a variable expansion
27 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
29 ** Permission to use, copy, modify, and distribute this software for
30 ** any purpose with or without fee is hereby granted, provided that
31 ** the above copyright notice and this permission notice appear in all
34 ** For disclaimer see below.
37 * Adapted by Kern Sibbald to Bacula June 2003
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--;
1870 /* ****FIXME**** passing back stack variable!!! */
1880 /* parse complex variable construct ("${name...}") */
1882 parse_variable_complex(
1883 var_t *var, var_parse_t *ctx,
1884 const char *begin, const char *end,
1889 int len, buffer_size;
1897 /* initializations */
1899 tokenbuf_init(&name);
1900 tokenbuf_init(&tmp);
1901 tokenbuf_init(result);
1903 /* parse open delimiter */
1904 if (p == end || *p != var->syntax.delim_open)
1908 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1910 /* parse name of variable to expand. The name may consist of an
1911 arbitrary number of variable name character and contained variable
1914 /* parse a variable name */
1915 rc = parse_name(var, ctx, p, end);
1919 if (!tokenbuf_append(&name, p, rc)) {
1920 rc = VAR_ERR_OUT_OF_MEMORY;
1926 /* parse an (embedded) variable */
1927 rc = parse_variable(var, ctx, p, end, &tmp);
1931 if (!tokenbuf_merge(&name, &tmp)) {
1932 rc = VAR_ERR_OUT_OF_MEMORY;
1937 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1940 /* we must have the complete expanded variable name now,
1941 so make sure we really do. */
1942 if (name.begin == name.end) {
1943 if (ctx->force_expand) {
1944 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1948 /* If no force_expand is requested, we have to back-off.
1949 We're not sure whether our approach here is 100% correct,
1950 because it _could_ have side-effects according to Peter
1951 Simons, but as far as we know and tried it, it is
1952 correct. But be warned -- RSE */
1953 tokenbuf_set(result, begin - 1, p, 0);
1958 /* parse an optional index specification */
1959 if ( var->syntax.index_open != EOS
1960 && *p == var->syntax.index_open) {
1962 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1966 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1971 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1974 if (*p != var->syntax.index_close) {
1975 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1981 /* parse end of variable construct or start of post-operations */
1982 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1983 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1986 if ((inc = (*p++ == '+'))) {
1987 p++; /* skip the + */
1990 /* lookup the variable value now */
1992 tokenbuf_set(result, begin - 1, p, 0);
1994 rc = lookup_value(var, ctx,
1995 name.begin, name.end-name.begin, inc, idx,
1996 &data, &len, &buffer_size);
1997 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1998 tokenbuf_init(result); /* delayed handling of undefined variable */
1999 } else if (rc < 0) {
2002 /* the preliminary result is the raw value of the variable.
2003 This may be modified by the operations that may follow. */
2004 tokenbuf_set(result, data, data + len, buffer_size);
2008 /* parse optional post-operations */
2011 tokenbuf_free(&tmp);
2012 tokenbuf_init(&tmp);
2014 while (p != end && *p == ':') {
2017 rc = parse_operation(var, ctx, p, end, result);
2019 rc = parse_operation(var, ctx, p, end, &tmp);
2026 if (p == end || *p != var->syntax.delim_close) {
2027 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2033 } else if (p[-1] == '+') {
2037 /* lazy handling of undefined variable */
2038 if (!failed && tokenbuf_isundef(result)) {
2039 if (ctx->force_expand) {
2040 rc = VAR_ERR_UNDEFINED_VARIABLE;
2043 tokenbuf_set(result, begin - 1, p, 0);
2047 /* return successfully */
2048 tokenbuf_free(&name);
2049 tokenbuf_free(&tmp);
2052 /* return with an error */
2054 tokenbuf_free(&name);
2055 tokenbuf_free(&tmp);
2056 tokenbuf_free(result);
2060 /* parse variable construct ("$name" or "${name...}") */
2063 var_t *var, var_parse_t *ctx,
2064 const char *begin, const char *end,
2069 int len, buffer_size;
2073 /* initialization */
2075 tokenbuf_init(result);
2077 /* parse init delimiter */
2078 if (p == end || *p != var->syntax.delim_init)
2082 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2084 /* parse a simple variable name.
2085 (if this fails, we're try to parse a complex variable construct) */
2086 rc = parse_name(var, ctx, p, end);
2090 inc = (p[rc] == '+');
2091 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2092 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2093 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2098 tokenbuf_set(result, data, data + len, buffer_size);
2102 /* parse a complex variable construct (else case) */
2103 rc = parse_variable_complex(var, ctx, p, end, result);
2109 /* parse loop construct limits ("[...]{b,s,e}") */
2112 var_t *var, var_parse_t *ctx,
2113 const char *begin, const char *end,
2114 int *start, int *step, int *stop, int *open_stop)
2120 /* initialization */
2123 /* we are happy if nothing is to left to parse */
2127 /* parse start delimiter */
2128 if (*p != var->syntax.delim_open)
2132 /* parse loop start value */
2134 rc = parse_numexp(var, ctx, p, end, start, &failed);
2135 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2136 *start = 0; /* use default */
2138 return (var_rc_t)rc;
2142 return VAR_ERR_UNDEFINED_VARIABLE;
2144 /* parse separator */
2146 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2149 /* parse loop step value */
2151 rc = parse_numexp(var, ctx, p, end, step, &failed);
2152 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2153 *step = 1; /* use default */
2155 return (var_rc_t)rc;
2159 return VAR_ERR_UNDEFINED_VARIABLE;
2161 /* parse separator */
2163 /* if not found, parse end delimiter */
2164 if (*p != var->syntax.delim_close)
2165 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2168 /* shift step value to stop value */
2172 /* determine whether loop end is open */
2177 return (var_rc_t)(p - begin);
2181 /* parse loop stop value */
2183 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2184 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2185 *stop = 0; /* use default */
2189 return (var_rc_t)rc;
2195 return VAR_ERR_UNDEFINED_VARIABLE;
2197 /* parse end delimiter */
2198 if (*p != var->syntax.delim_close)
2199 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2202 /* return amount of parsed input */
2203 return (var_rc_t)(p - begin);
2206 /* parse plain text */
2209 var_t *var, var_parse_t *ctx,
2210 const char *begin, const char *end)
2214 /* parse until delim_init (variable construct)
2215 or index_open (loop construct) is found */
2216 for (p = begin; p != end; p++) {
2217 if (*p == var->syntax.escape) {
2218 p++; /* skip next character */
2220 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2222 else if (*p == var->syntax.delim_init)
2224 else if ( var->syntax.index_open != EOS
2225 && ( *p == var->syntax.index_open
2226 || *p == var->syntax.index_close))
2232 /* expand input in general */
2235 var_t *var, var_parse_t *ctx,
2236 const char *begin, const char *end,
2237 tokenbuf_t *output, int recursion_level)
2242 int start, step, stop, open_stop;
2246 int loop_limit_length;
2249 /* initialization */
2253 /* try to parse a loop construct */
2255 && var->syntax.index_open != EOS
2256 && *p == var->syntax.index_open) {
2259 /* loop preparation */
2260 loop_limit_length = -1;
2261 rel_lookup_cnt = ctx->rel_lookup_cnt;
2269 /* iterate over loop construct, either as long as there is
2270 (still) nothing known about the limit, or there is an open
2271 (=unknown) limit stop and there are still defined variables
2272 or there is a stop limit known and it is still not reached */
2276 && ( loop_limit_length < 0
2277 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2282 /* remember current output end for restoring */
2283 output_backup = (output->end - output->begin);
2285 /* open temporary context for recursion */
2286 ctx = var_parse_push(ctx, &myctx);
2287 ctx->force_expand = 1;
2288 ctx->rel_lookup_flag = 1;
2289 ctx->index_this = i;
2291 /* recursive parse input through ourself */
2292 rc = parse_input(var, ctx, p, end,
2293 output, recursion_level+1);
2295 /* retrieve info and close temporary context */
2296 rel_lookup_cnt = ctx->rel_lookup_cnt;
2297 ctx = var_parse_pop(ctx);
2299 /* error handling */
2303 /* make sure the loop construct is closed */
2304 if (p[rc] != var->syntax.index_close) {
2305 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2309 /* try to parse loop construct limit specification */
2310 if (loop_limit_length < 0) {
2311 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2312 &start, &step, &stop, &open_stop);
2316 loop_limit_length = 0;
2318 loop_limit_length = rc2;
2319 /* restart loop from scratch */
2320 output->end = (output->begin + output_backup);
2326 /* if stop value is open, restore to the output end
2327 because the last iteration was just to determine the loop
2328 termination and its result has to be discarded */
2330 output->end = (output->begin + output_backup);
2332 /* skip parsed loop construct */
2335 p += loop_limit_length;
2340 /* try to parse plain text */
2341 rc = parse_text(var, ctx, p, end);
2343 if (!tokenbuf_append(output, p, rc)) {
2344 rc = VAR_ERR_OUT_OF_MEMORY;
2352 /* try to parse a variable construct */
2353 tokenbuf_init(&result);
2354 rc = parse_variable(var, ctx, p, end, &result);
2356 if (!tokenbuf_merge(output, &result)) {
2357 tokenbuf_free(&result);
2358 rc = VAR_ERR_OUT_OF_MEMORY;
2361 tokenbuf_free(&result);
2365 tokenbuf_free(&result);
2369 } while (p != end && rc > 0);
2371 /* We do not know whether this really could happen, but because we
2372 are paranoid, report an error at the outer most parsing level if
2373 there is still any input. Because this would mean that we are no
2374 longer able to parse the remaining input as a loop construct, a
2375 text or a variable construct. This would be very strange, but
2376 could perhaps happen in case of configuration errors!?... */
2377 if (recursion_level == 0 && p != end) {
2378 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2382 /* return amount of parsed text */
2383 return (var_rc_t)(p - begin);
2385 /* return with an error where as a special case the output begin is
2386 set to the input begin and the output end to the last input parsing
2389 tokenbuf_free(output);
2390 tokenbuf_set(output, begin, p, 0);
2391 return (var_rc_t)rc;
2396 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2400 /* create variable expansion context */
2408 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2409 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2410 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2411 memset(var, 0, sizeof(var_t));
2412 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2417 /* destroy variable expansion context */
2423 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2428 /* configure variable expansion context */
2436 var_rc_t rc = VAR_OK;
2439 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2442 case VAR_CONFIG_SYNTAX: {
2444 s = (var_syntax_t *)va_arg(ap, void *);
2446 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2447 var->syntax.escape = s->escape;
2448 var->syntax.delim_init = s->delim_init;
2449 var->syntax.delim_open = s->delim_open;
2450 var->syntax.delim_close = s->delim_close;
2451 var->syntax.index_open = s->index_open;
2452 var->syntax.index_close = s->index_close;
2453 var->syntax.index_mark = s->index_mark;
2454 var->syntax.name_chars = NULL; /* unused internally */
2455 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2457 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2458 || var->syntax_nameclass[(int)var->syntax.delim_open]
2459 || var->syntax_nameclass[(int)var->syntax.delim_close]
2460 || var->syntax_nameclass[(int)var->syntax.escape])
2461 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2464 case VAR_CONFIG_CB_VALUE: {
2467 fct = (var_cb_value_t)va_arg(ap, void *);
2468 ctx = (void *)va_arg(ap, void *);
2469 var->cb_value_fct = fct;
2470 var->cb_value_ctx = ctx;
2473 case VAR_CONFIG_CB_OPERATION: {
2474 var_cb_operation_t fct;
2476 fct = (var_cb_operation_t)va_arg(ap, void *);
2477 ctx = (void *)va_arg(ap, void *);
2478 var->cb_operation_fct = fct;
2479 var->cb_operation_ctx = ctx;
2483 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2489 /* perform unescape operation on a buffer */
2493 const char *src, int srclen,
2494 char *dst, int dstlen,
2500 if (var == NULL || src == NULL || dst == NULL)
2501 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2506 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2525 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2528 case '0': case '1': case '2': case '3': case '4':
2529 case '5': case '6': case '7': case '8': case '9':
2531 && isdigit((int)src[1])
2532 && isdigit((int)src[2])) {
2533 if ((rc = expand_octal(&src, &dst, end)) != 0)
2551 /* perform expand operation on a buffer */
2555 const char *src_ptr, int src_len,
2556 char **dst_ptr, int *dst_len,
2563 /* argument sanity checks */
2564 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2565 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2567 /* prepare internal expansion context */
2569 ctx.force_expand = force_expand;
2570 ctx.rel_lookup_flag = 0;
2571 ctx.rel_lookup_cnt = 0;
2574 /* start the parsing */
2575 tokenbuf_init(&output);
2576 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2578 /* post-processing */
2580 /* always EOS-terminate output for convinience reasons
2581 but do not count the EOS-terminator in the length */
2582 if (!tokenbuf_append(&output, "\0", 1)) {
2583 tokenbuf_free(&output);
2584 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2588 /* provide result */
2589 *dst_ptr = (char *)output.begin;
2590 if (dst_len != NULL)
2591 *dst_len = (output.end - output.begin);
2595 /* provide result */
2596 if (dst_len != NULL)
2597 *dst_len = (output.end - output.begin);
2603 /* format and expand a string */
2607 char **dst_ptr, int force_expand,
2608 const char *fmt, va_list ap)
2614 /* argument sanity checks */
2615 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2616 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2618 /* perform formatting */
2619 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2620 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2621 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2624 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2627 /* perform expansion */
2628 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2639 /* format and expand a string */
2643 char **dst_ptr, int force_expand,
2644 const char *fmt, ...)
2649 /* argument sanity checks */
2650 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2651 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2654 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2660 /* var_rc_t to string mapping table */
2661 static const char *var_errors[] = {
2662 _("everything ok"), /* VAR_OK = 0 */
2663 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2664 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2665 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2666 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2667 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2668 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2669 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2670 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2671 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2672 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2673 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2674 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2675 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2676 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2677 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2678 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2679 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2680 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2681 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2682 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2683 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2684 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2685 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2686 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2687 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2688 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2689 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2690 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2691 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2692 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2693 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2694 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2695 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2696 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2697 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2698 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2699 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2700 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2701 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2702 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2703 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2704 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2705 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2706 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2707 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2710 /* translate a return code into its corresponding descriptive text */
2711 const char *var_strerror(var_t *var, var_rc_t rc)
2714 rc = (var_rc_t)(0 - rc);
2715 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2716 str = _("unknown error");
2718 str = (char *)var_errors[rc];