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 Bacula® - The Network Backup Solution
23 Copyright (C) 2003-2006 Free Software Foundation Europe e.V.
25 The main author of Bacula is Kern Sibbald, with contributions from
26 many others, a complete list can be found in the file AUTHORS.
27 This program is Free Software; you can redistribute it and/or
28 modify it under the terms of version two of the GNU General Public
29 License as published by the Free Software Foundation plus additions
30 that are listed in the file LICENSE.
32 This program is distributed in the hope that it will be useful, but
33 WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35 General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program; if not, write to the Free Software
39 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
42 Bacula® is a registered trademark of John Walker.
43 The licensor of Bacula is the Free Software Foundation Europe
44 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
45 Switzerland, email:ftf@fsfeurope.org.
49 #if defined(HAVE_PCREPOSIX)
50 # include <pcreposix.h>
51 #elif defined(HAVE_WIN32)
58 /* support for OSSP ex based exception throwing */
62 ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \
63 ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
65 #define VAR_RC(rv) (rv)
74 ** ==== INTERNAL DATA STRUCTURES ====
78 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
80 /* the external context structure */
83 char_class_t syntax_nameclass;
84 var_cb_value_t cb_value_fct;
86 var_cb_operation_t cb_operation_fct;
87 void *cb_operation_ctx;
90 /* the internal expansion context structure */
92 struct var_parse_st *lower;
98 typedef struct var_parse_st var_parse_t;
100 /* the default syntax configuration */
101 static const var_syntax_t var_syntax_default = {
103 '$', /* delim_init */
104 '{', /* delim_open */
105 '}', /* delim_close */
106 '[', /* index_open */
107 ']', /* index_close */
108 '#', /* index_mark */
109 "a-zA-Z0-9_" /* name_chars */
114 ** ==== FORMATTING FUNCTIONS ====
118 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
121 int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx,
122 const char *format, va_list ap)
124 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
125 char ibuf[((sizeof(int)*8)/3)+10];
135 while (*format != '\0') {
136 if (*format == '%') {
145 c = (char)va_arg(ap, int);
151 if ((cp = (char *)va_arg(ap, char *)) == NULL)
157 d = (int)va_arg(ap, int);
158 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
172 if ((format = strchr(cp, '%')) == NULL)
173 format = strchr(cp, '\0');
176 /* perform output operation */
178 if ((n = output(ctx, cp, n)) == -1)
185 /* output callback function context for var_mvsnprintf() */
189 } var_mvsnprintf_cb_t;
191 /* output callback function for var_mvsnprintf() */
195 const char *buffer, int bufsize)
197 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
199 if (bufsize > ctx->buflen)
201 memcpy(ctx->bufptr, buffer, bufsize);
202 ctx->bufptr += bufsize;
203 ctx->buflen -= bufsize;
207 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
210 char *buffer, int bufsize,
211 const char *format, va_list ap)
214 var_mvsnprintf_cb_t ctx;
218 if (buffer != NULL && bufsize == 0)
221 /* just determine output length */
222 n = var_mvxprintf(NULL, NULL, format, ap);
224 /* perform real output */
226 ctx.buflen = bufsize;
227 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
228 if (n != -1 && ctx.buflen == 0)
231 *(ctx.bufptr) = '\0';
238 ** ==== PARSE CONTEXT FUNCTIONS ====
244 var_parse_t *lower, var_parse_t *upper)
248 memcpy(upper, lower, sizeof(var_parse_t));
249 upper->lower = lower;
264 ** ==== TOKEN BUFFER FUNCTIONS ====
268 #define TOKENBUF_INITIAL_BUFSIZE 64
282 buf->buffer_size = 0;
290 if (buf->begin == NULL && buf->end == NULL)
299 if (buf->begin == buf->end)
306 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
310 buf->buffer_size = buffer_size;
316 tokenbuf_t *src, tokenbuf_t *dst)
318 dst->begin = src->begin;
320 dst->buffer_size = src->buffer_size;
327 tokenbuf_t *buf, const char *data, int len)
331 if ((p = (char *)malloc(len + 1)) == NULL)
333 memcpy(p, data, len);
336 buf->buffer_size = len + 1;
337 *((char *)(buf->end)) = EOS;
343 tokenbuf_t *output, const char *data, int len)
349 /* Is the tokenbuffer initialized at all? If not, allocate a
350 standard-sized buffer to begin with. */
351 if (output->begin == NULL) {
352 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
354 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
357 /* does the token contain text, but no buffer has been allocated yet? */
358 if (output->buffer_size == 0) {
359 /* check whether data borders to output. If, we can append
360 simly by increasing the end pointer. */
361 if (output->end == data) {
365 /* ok, so copy the contents of output into an allocated buffer
366 so that we can append that way. */
367 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
369 memcpy(tmp, output->begin, output->end - output->begin);
370 output->buffer_size = output->end - output->begin;
372 output->end = tmp + output->buffer_size;
373 output->buffer_size += len + 1;
376 /* does the token fit into the current buffer? If not, realloc a
377 larger buffer that fits. */
378 if ((output->buffer_size - (output->end - output->begin)) <= len) {
379 new_size = output->buffer_size;
382 } while ((new_size - (output->end - output->begin)) <= len);
383 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
385 output->end = new_buffer + (output->end - output->begin);
386 output->begin = new_buffer;
387 output->buffer_size = new_size;
390 /* append the data at the end of the current buffer. */
392 memcpy((char *)output->end, data, len);
394 *((char *)output->end) = EOS;
400 tokenbuf_t *output, tokenbuf_t *input)
402 return tokenbuf_append(output, input->begin, input->end - input->begin);
409 if (buf->begin != NULL && buf->buffer_size > 0)
410 free((char *)buf->begin);
411 buf->begin = buf->end = NULL;
412 buf->buffer_size = 0;
418 ** ==== CHARACTER CLASS EXPANSION ====
423 expand_range(char a, char b, char_class_t chrclass)
426 chrclass[(int)a] = 1;
432 expand_character_class(const char *desc, char_class_t chrclass)
436 /* clear the class array. */
437 for (i = 0; i < 256; ++i)
440 /* walk through class description and set appropriate entries in array */
441 while (*desc != EOS) {
442 if (desc[1] == '-' && desc[2] != EOS) {
443 if (desc[0] > desc[2])
444 return VAR_ERR_INCORRECT_CLASS_SPEC;
445 expand_range(desc[0], desc[2], chrclass);
448 chrclass[(int) *desc] = 1;
457 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
465 if (c >= '0' && c <= '7')
473 const char **src, char **dst, const char *end)
478 return VAR_ERR_INCOMPLETE_OCTAL;
479 if ( !expand_isoct(**src)
480 || !expand_isoct((*src)[1])
481 || !expand_isoct((*src)[2]))
482 return VAR_ERR_INVALID_OCTAL;
486 return VAR_ERR_OCTAL_TOO_LARGE;
505 if ((c >= '0' && c <= '9') ||
506 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
514 const char **src, char **dst, const char *end)
519 return VAR_ERR_INCOMPLETE_HEX;
520 if ( !expand_ishex(**src)
521 || !expand_ishex((*src)[1]))
522 return VAR_ERR_INVALID_HEX;
524 if (**src >= '0' && **src <= '9')
526 else if (**src >= 'a' && **src <= 'f')
527 c = **src - 'a' + 10;
528 else if (**src >= 'A' && **src <= 'F')
529 c = **src - 'A' + 10;
534 if (**src >= '0' && **src <= '9')
536 else if (**src >= 'a' && **src <= 'f')
537 c += **src - 'a' + 10;
538 else if (**src >= 'A' && **src <= 'F')
539 c += **src - 'A' + 10;
548 const char **src, char **dst, const char *end)
552 while (*src < end && **src != '}') {
553 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
558 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
565 const char **src, char **dst, const char *end)
568 return VAR_ERR_INCOMPLETE_HEX;
571 return expand_grouped_hex(src, dst, end);
573 return expand_simple_hex(src, dst, end);
578 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
582 /* forward declarations */
583 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
584 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
585 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
587 /* parse pattern text */
590 var_t *var, var_parse_t *ctx,
591 const char *begin, const char *end)
595 /* parse until '/' */
596 for (p = begin; p != end && *p != '/'; p++) {
597 if (*p == var->syntax.escape) {
599 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
606 /* parse substitution text */
609 var_t *var, var_parse_t *ctx,
610 const char *begin, const char *end)
614 /* parse until delim_init or '/' */
615 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
616 if (*p == var->syntax.escape) {
618 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
625 /* parse expression text */
628 var_t *var, var_parse_t *ctx,
629 const char *begin, const char *end)
633 /* parse until delim_init or delim_close or ':' */
634 for (p = begin; p != end
635 && *p != var->syntax.delim_init
636 && *p != var->syntax.delim_close
638 if (*p == var->syntax.escape) {
640 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
647 /* parse opertion argument text */
650 var_t *var, var_parse_t *ctx,
651 const char *begin, const char *end)
655 /* parse until delim_init or ')' */
656 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
657 if (*p == var->syntax.escape) {
659 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
667 parse_opargtext_or_variable(
668 var_t *var, var_parse_t *ctx,
669 const char *begin, const char *end,
676 tokenbuf_init(result);
682 rc = parse_opargtext(var, ctx, p, end);
686 if (!tokenbuf_append(result, p, rc)) {
687 rc = VAR_ERR_OUT_OF_MEMORY;
692 rc = parse_variable(var, ctx, p, end, &tmp);
697 if (!tokenbuf_merge(result, &tmp)) {
698 rc = VAR_ERR_OUT_OF_MEMORY;
702 tokenbuf_free(&tmp); /* KES 11/9/2003 */
709 tokenbuf_free(result);
713 /* parse expression or variable */
715 parse_exptext_or_variable(
716 var_t *var, var_parse_t *ctx,
717 const char *begin, const char *end,
720 const char *p = begin;
724 tokenbuf_init(result);
729 /* try to parse expression text */
730 rc = parse_exptext(var, ctx, p, end);
734 if (!tokenbuf_append(result, p, rc)) {
735 rc = VAR_ERR_OUT_OF_MEMORY;
741 /* try to parse variable construct */
742 rc = parse_variable(var, ctx, p, end, &tmp);
747 if (!tokenbuf_merge(result, &tmp)) {
748 rc = VAR_ERR_OUT_OF_MEMORY;
752 tokenbuf_free(&tmp); /* KES 11/9/2003 */
760 tokenbuf_free(result);
764 /* parse substitution text or variable */
766 parse_substext_or_variable(
767 var_t *var, var_parse_t *ctx,
768 const char *begin, const char *end,
771 const char *p = begin;
775 tokenbuf_init(result);
780 /* try to parse substitution text */
781 rc = parse_substext(var, ctx, p, end);
785 if (!tokenbuf_append(result, p, rc)) {
786 rc = VAR_ERR_OUT_OF_MEMORY;
792 /* try to parse substitution text */
793 rc = parse_variable(var, ctx, p, end, &tmp);
798 if (!tokenbuf_merge(result, &tmp)) {
799 rc = VAR_ERR_OUT_OF_MEMORY;
803 tokenbuf_free(&tmp); /* KES 11/9/2003 */
811 tokenbuf_free(result);
815 /* parse class description */
817 parse_class_description(
818 var_t *var, var_parse_t *ctx,
819 tokenbuf_t *src, tokenbuf_t *dst)
825 while (p != src->end) {
826 if ((src->end - p) >= 3 && p[1] == '-') {
828 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
829 for (c = *p, d = p[2]; c <= d; ++c) {
830 if (!tokenbuf_append(dst, (char *)&c, 1))
831 return VAR_ERR_OUT_OF_MEMORY;
835 if (!tokenbuf_append(dst, p, 1))
836 return VAR_ERR_OUT_OF_MEMORY;
843 /* parse regex replace part */
846 var_t *var, var_parse_t *ctx,
850 tokenbuf_t *expanded)
856 tokenbuf_init(expanded);
858 while (p != orig->end) {
860 if (orig->end - p <= 1) {
861 tokenbuf_free(expanded);
862 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
866 if (!tokenbuf_append(expanded, p, 1)) {
867 tokenbuf_free(expanded);
868 return VAR_ERR_OUT_OF_MEMORY;
873 if (!isdigit((int)*p)) {
874 tokenbuf_free(expanded);
875 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
879 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
880 tokenbuf_free(expanded);
881 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
883 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
884 pmatch[i].rm_eo - pmatch[i].rm_so)) {
885 tokenbuf_free(expanded);
886 return VAR_ERR_OUT_OF_MEMORY;
889 if (!tokenbuf_append(expanded, p, 1)) {
890 tokenbuf_free(expanded);
891 return VAR_ERR_OUT_OF_MEMORY;
900 /* operation: transpose */
903 var_t *var, var_parse_t *ctx,
908 tokenbuf_t srcclass, dstclass;
913 tokenbuf_init(&srcclass);
914 tokenbuf_init(&dstclass);
915 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
917 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
919 if (srcclass.begin == srcclass.end) {
920 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
923 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
924 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
927 if (data->buffer_size == 0) {
929 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
930 rc = VAR_ERR_OUT_OF_MEMORY;
933 tokenbuf_move(&tmp, data);
935 for (p = data->begin; p != data->end; ++p) {
936 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
937 if (*p == srcclass.begin[i]) {
938 *((char *)p) = dstclass.begin[i];
943 tokenbuf_free(&srcclass);
944 tokenbuf_free(&dstclass);
948 tokenbuf_free(search);
949 tokenbuf_free(replace);
950 tokenbuf_free(&srcclass);
951 tokenbuf_free(&dstclass);
955 /* operation: search & replace */
957 op_search_and_replace(
958 var_t *var, var_parse_t *ctx,
966 int case_insensitive = 0;
972 if (search->begin == search->end)
973 return VAR_ERR_EMPTY_SEARCH_STRING;
975 for (p = flags->begin; p != flags->end; p++) {
976 switch (tolower(*p)) {
981 case_insensitive = 1;
990 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
995 /* plain text pattern based operation */
997 for (p = data->begin; p != data->end;) {
998 if (case_insensitive)
999 rc = strncasecmp(p, search->begin, search->end - search->begin);
1001 rc = strncmp(p, search->begin, search->end - search->begin);
1003 /* not matched, copy character */
1004 if (!tokenbuf_append(&tmp, p, 1)) {
1005 tokenbuf_free(&tmp);
1006 return VAR_ERR_OUT_OF_MEMORY;
1010 /* matched, copy replacement string */
1011 tokenbuf_merge(&tmp, replace);
1012 p += (search->end - search->begin);
1014 /* append remaining text */
1015 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1016 tokenbuf_free(&tmp);
1017 return VAR_ERR_OUT_OF_MEMORY;
1023 tokenbuf_free(data);
1024 tokenbuf_move(&tmp, data);
1026 /* regular expression pattern based operation */
1028 tokenbuf_t myreplace;
1030 regmatch_t pmatch[10];
1033 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1034 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1035 return VAR_ERR_OUT_OF_MEMORY;
1036 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1037 tokenbuf_free(&tmp);
1038 return VAR_ERR_OUT_OF_MEMORY;
1041 /* compile the pattern. */
1042 rc = regcomp(&preg, tmp.begin,
1044 | (multiline ? REG_NEWLINE : 0)
1045 | (case_insensitive ? REG_ICASE : 0)));
1046 tokenbuf_free(&tmp);
1048 tokenbuf_free(&mydata);
1049 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1052 /* match the pattern and create the result string in the tmp buffer */
1053 tokenbuf_append(&tmp, "", 0);
1054 for (p = mydata.begin; p < mydata.end; ) {
1055 if (p == mydata.begin || p[-1] == '\n')
1058 regexec_flag = REG_NOTBOL;
1059 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1061 /* no (more) matching */
1062 tokenbuf_append(&tmp, p, mydata.end - p);
1066 && (p + pmatch[0].rm_so) == mydata.end
1067 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1068 /* special case: found empty pattern (usually /^/ or /$/ only)
1069 in multi-line at end of data (after the last newline) */
1070 tokenbuf_append(&tmp, p, mydata.end - p);
1074 /* append prolog string */
1075 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1077 tokenbuf_free(&tmp);
1078 tokenbuf_free(&mydata);
1079 return VAR_ERR_OUT_OF_MEMORY;
1081 /* create replace string */
1082 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1085 tokenbuf_free(&tmp);
1086 tokenbuf_free(&mydata);
1089 /* append replace string */
1090 if (!tokenbuf_merge(&tmp, &myreplace)) {
1092 tokenbuf_free(&tmp);
1093 tokenbuf_free(&mydata);
1094 tokenbuf_free(&myreplace);
1095 return VAR_ERR_OUT_OF_MEMORY;
1097 tokenbuf_free(&myreplace);
1098 /* skip now processed data */
1099 p += pmatch[0].rm_eo;
1100 /* if pattern matched an empty part (think about
1101 anchor-only regular expressions like /^/ or /$/) we
1102 skip the next character to make sure we do not enter
1103 an infinitive loop in matching */
1104 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1105 if (p >= mydata.end)
1107 if (!tokenbuf_append(&tmp, p, 1)) {
1109 tokenbuf_free(&tmp);
1110 tokenbuf_free(&mydata);
1111 return VAR_ERR_OUT_OF_MEMORY;
1115 /* append prolog string and stop processing if we
1116 do not perform the search & replace globally */
1118 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1120 tokenbuf_free(&tmp);
1121 tokenbuf_free(&mydata);
1122 return VAR_ERR_OUT_OF_MEMORY;
1129 tokenbuf_free(data);
1130 tokenbuf_move(&tmp, data);
1131 tokenbuf_free(&mydata);
1137 /* operation: offset substring */
1140 var_t *var, var_parse_t *ctx,
1149 /* determine begin of result string */
1150 if ((data->end - data->begin) < num1)
1151 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1152 p = data->begin + num1;
1154 /* if num2 is zero, we copy the rest from there. */
1156 if (!tokenbuf_assign(&res, p, data->end - p))
1157 return VAR_ERR_OUT_OF_MEMORY;
1159 /* ok, then use num2. */
1161 if ((p + num2) > data->end)
1162 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1163 if (!tokenbuf_assign(&res, p, num2))
1164 return VAR_ERR_OUT_OF_MEMORY;
1167 return VAR_ERR_OFFSET_LOGIC;
1168 if ((data->begin + num2) > data->end)
1169 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1170 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1171 return VAR_ERR_OUT_OF_MEMORY;
1174 tokenbuf_free(data);
1175 tokenbuf_move(&res, data);
1179 /* operation: padding */
1182 var_t *var, var_parse_t *ctx,
1191 if (fill->begin == fill->end)
1192 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1193 tokenbuf_init(&result);
1194 if (position == 'l') {
1196 i = width - (data->end - data->begin);
1198 i = i / (fill->end - fill->begin);
1200 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1201 return VAR_ERR_OUT_OF_MEMORY;
1204 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1205 if (!tokenbuf_append(data, fill->begin, i))
1206 return VAR_ERR_OUT_OF_MEMORY;
1208 } else if (position == 'r') {
1210 i = width - (data->end - data->begin);
1212 i = i / (fill->end - fill->begin);
1214 if (!tokenbuf_merge(&result, fill)) {
1215 tokenbuf_free(&result);
1216 return VAR_ERR_OUT_OF_MEMORY;
1220 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1221 if (!tokenbuf_append(&result, fill->begin, i)) {
1222 tokenbuf_free(&result);
1223 return VAR_ERR_OUT_OF_MEMORY;
1225 if (!tokenbuf_merge(&result, data)) {
1226 tokenbuf_free(&result);
1227 return VAR_ERR_OUT_OF_MEMORY;
1229 /* move string from temporary buffer to data buffer */
1230 tokenbuf_free(data);
1231 tokenbuf_move(&result, data);
1233 } else if (position == 'c') {
1234 /* centered padding */
1235 i = (width - (data->end - data->begin)) / 2;
1237 /* create the prefix */
1238 i = i / (fill->end - fill->begin);
1240 if (!tokenbuf_merge(&result, fill)) {
1241 tokenbuf_free(&result);
1242 return VAR_ERR_OUT_OF_MEMORY;
1246 i = ((width - (data->end - data->begin)) / 2)
1247 % (fill->end - fill->begin);
1248 if (!tokenbuf_append(&result, fill->begin, i)) {
1249 tokenbuf_free(&result);
1250 return VAR_ERR_OUT_OF_MEMORY;
1252 /* append the actual data string */
1253 if (!tokenbuf_merge(&result, data)) {
1254 tokenbuf_free(&result);
1255 return VAR_ERR_OUT_OF_MEMORY;
1257 /* append the suffix */
1258 i = width - (result.end - result.begin);
1259 i = i / (fill->end - fill->begin);
1261 if (!tokenbuf_merge(&result, fill)) {
1262 tokenbuf_free(&result);
1263 return VAR_ERR_OUT_OF_MEMORY;
1267 i = width - (result.end - result.begin);
1268 if (!tokenbuf_append(&result, fill->begin, i)) {
1269 tokenbuf_free(&result);
1270 return VAR_ERR_OUT_OF_MEMORY;
1272 /* move string from temporary buffer to data buffer */
1273 tokenbuf_free(data);
1274 tokenbuf_move(&result, data);
1280 /* parse an integer number ("123") */
1283 var_t *var, var_parse_t *ctx,
1284 const char *begin, const char *end,
1292 while (isdigit(*p) && p != end) {
1302 /* parse an operation (":x...") */
1305 var_t *var, var_parse_t *ctx,
1306 const char *begin, const char *end,
1310 tokenbuf_t tmptokbuf;
1311 tokenbuf_t search, replace, flags;
1312 tokenbuf_t number1, number2;
1318 /* initialization */
1319 tokenbuf_init(&tmptokbuf);
1320 tokenbuf_init(&search);
1321 tokenbuf_init(&replace);
1322 tokenbuf_init(&flags);
1323 tokenbuf_init(&number1);
1324 tokenbuf_init(&number2);
1329 /* dispatch through the first operation character */
1330 switch (tolower(*p)) {
1332 /* turn value to lowercase. */
1333 if (data->begin != NULL) {
1334 /* if the buffer does not live in an allocated buffer,
1335 we have to copy it before modifying the contents. */
1336 if (data->buffer_size == 0) {
1337 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1338 rc = VAR_ERR_OUT_OF_MEMORY;
1343 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1344 *ptr = (char)tolower((int)(*ptr));
1350 /* turn value to uppercase. */
1351 if (data->begin != NULL) {
1352 /* if the buffer does not live in an allocated buffer,
1353 we have to copy it before modifying the contents. */
1354 if (data->buffer_size == 0) {
1355 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1356 rc = VAR_ERR_OUT_OF_MEMORY;
1361 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1362 *ptr = (char)toupper((int)(*ptr));
1368 /* cut out substring of value. */
1370 rc = parse_integer(var, ctx, p, end, &num1);
1372 rc = VAR_ERR_MISSING_START_OFFSET;
1381 } else if (*p == '-') {
1385 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1388 rc = parse_integer(var, ctx, p, end, &num2);
1390 if (data->begin != NULL) {
1391 rc = op_offset(var, ctx, data, num1, num2, isrange);
1398 /* determine length of the value */
1399 if (data->begin != NULL) {
1400 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1401 sprintf(buf, "%d", (int)(data->end - data->begin));
1402 tokenbuf_free(data);
1403 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1404 rc = VAR_ERR_OUT_OF_MEMORY;
1412 /* substitute parameter if data is empty */
1414 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1418 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1422 if (tokenbuf_isundef(data))
1423 tokenbuf_move(&tmptokbuf, data);
1424 else if (tokenbuf_isempty(data)) {
1425 tokenbuf_free(data);
1426 tokenbuf_move(&tmptokbuf, data);
1431 /* substitute empty string if data is not empty, parameter otherwise. */
1433 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1437 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1441 if (data->begin != NULL) {
1442 if (data->begin == data->end) {
1443 tokenbuf_free(data);
1444 tokenbuf_move(&tmptokbuf, data);
1446 tokenbuf_free(data);
1447 data->begin = data->end = "";
1448 data->buffer_size = 0;
1454 /* substitute parameter if data is not empty. */
1456 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1460 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1464 if (data->begin != NULL && data->begin != data->end) {
1465 tokenbuf_free(data);
1466 tokenbuf_move(&tmptokbuf, data);
1471 /* search and replace. */
1474 return VAR_ERR_MALFORMATTED_REPLACE;
1476 rc = parse_pattern(var, ctx, p, end);
1479 tokenbuf_set(&search, p, p + rc, 0);
1482 rc = VAR_ERR_MALFORMATTED_REPLACE;
1486 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1491 rc = VAR_ERR_MALFORMATTED_REPLACE;
1495 rc = parse_exptext(var, ctx, p, end);
1498 tokenbuf_set(&flags, p, p + rc, 0);
1500 if (data->begin != NULL) {
1501 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1508 /* transpose characters from class A to class B. */
1511 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1513 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1518 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1522 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1527 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1532 rc = op_transpose(var, ctx, data, &search, &replace);
1542 return VAR_ERR_MALFORMATTED_PADDING;
1544 rc = parse_integer(var, ctx, p, end, &num1);
1546 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1551 rc = VAR_ERR_MALFORMATTED_PADDING;
1555 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1560 rc = VAR_ERR_MALFORMATTED_PADDING;
1564 if (*p != 'l' && *p != 'c' && *p != 'r') {
1565 rc = VAR_ERR_MALFORMATTED_PADDING;
1570 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1577 /* operation callback function */
1580 const char *arg_ptr;
1582 const char *val_ptr;
1584 const char *out_ptr;
1590 rc = parse_name(var, ctx, p, end);
1598 tokenbuf_init(&args);
1599 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1603 arg_ptr = args.begin;
1604 arg_len = args.end - args.begin;
1606 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1615 val_ptr = data->begin;
1616 val_len = data->end - data->begin;
1618 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1619 /* call operation callback function */
1620 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1624 &out_ptr, &out_len, &out_size);
1626 if (arg_ptr != NULL)
1627 free((void *)arg_ptr);
1630 tokenbuf_free(data);
1631 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1633 if (arg_ptr != NULL)
1634 free((void *)arg_ptr);
1638 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1641 /* return successfully */
1642 tokenbuf_free(&tmptokbuf);
1643 tokenbuf_free(&search);
1644 tokenbuf_free(&replace);
1645 tokenbuf_free(&flags);
1646 tokenbuf_free(&number1);
1647 tokenbuf_free(&number2);
1650 /* return with an error */
1652 tokenbuf_free(data);
1653 tokenbuf_free(&tmptokbuf);
1654 tokenbuf_free(&search);
1655 tokenbuf_free(&replace);
1656 tokenbuf_free(&flags);
1657 tokenbuf_free(&number1);
1658 tokenbuf_free(&number2);
1662 /* parse numerical expression operand */
1664 parse_numexp_operand(
1665 var_t *var, var_parse_t *ctx,
1666 const char *begin, const char *end,
1667 int *result, int *failed)
1674 /* initialization */
1676 tokenbuf_init(&tmp);
1678 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1680 /* parse opening numerical expression */
1682 /* parse inner numerical expression */
1683 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1688 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1689 /* parse closing parenthesis */
1691 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1694 /* parse contained variable */
1695 else if (*p == var->syntax.delim_init) {
1696 /* parse variable with forced expansion */
1697 ctx = var_parse_push(ctx, &myctx);
1698 ctx->force_expand = 1;
1699 rc = parse_variable(var, ctx, p, end, &tmp);
1700 ctx = var_parse_pop(ctx);
1702 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1704 /* parse variable without forced expansion */
1705 ctx = var_parse_push(ctx, &myctx);
1706 ctx->force_expand = 0;
1707 rc = parse_variable(var, ctx, p, end, &tmp);
1708 ctx = var_parse_pop(ctx);
1713 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1714 } else if (rc < 0) {
1718 /* parse remaining numerical expression */
1719 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1720 tokenbuf_free(&tmp);
1725 /* parse relative index mark ("#") */
1726 else if ( var->syntax.index_mark != EOS
1727 && *p == var->syntax.index_mark) {
1729 *result = ctx->index_this;
1730 if (ctx->rel_lookup_flag)
1731 ctx->rel_lookup_cnt++;
1733 /* parse plain integer number */
1734 else if (isdigit(*p)) {
1735 rc = parse_integer(var, ctx, p, end, result);
1738 /* parse signed positive integer number */
1739 else if (*p == '+') {
1740 if ((end - p) > 1 && isdigit(p[1])) {
1742 rc = parse_integer(var, ctx, p, end, result);
1746 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1748 /* parse signed negative integer number */
1749 else if (*p == '-') {
1750 if (end - p > 1 && isdigit(p[1])) {
1752 rc = parse_integer(var, ctx, p, end, result);
1753 *result = -(*result);
1757 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1759 /* else we failed to parse anything reasonable */
1761 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1766 /* parse numerical expression ("x+y") */
1769 var_t *var, var_parse_t *ctx,
1770 const char *begin, const char *end,
1771 int *result, int *failed)
1778 /* initialization */
1781 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1783 /* parse left numerical operand */
1784 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1789 /* parse numerical operator */
1791 if (*p == '+' || *p == '-') {
1793 /* recursively parse right operand (light binding) */
1794 rc = parse_numexp(var, ctx, p, end, &right, failed);
1799 *result = (*result + right);
1801 *result = (*result - right);
1803 else if (*p == '*' || *p == '/' || *p == '%') {
1805 /* recursively parse right operand (string binding) */
1806 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1811 *result = (*result * right);
1812 else if (op == '/') {
1817 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1820 *result = (*result / right);
1822 else if (op == '%') {
1827 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1830 *result = (*result % right);
1837 /* return amount of parsed input */
1841 /* parse variable name ("abc") */
1844 var_t *var, var_parse_t *ctx,
1845 const char *begin, const char *end)
1849 /* parse as long as name class characters are found */
1850 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1855 /* lookup a variable value through the callback function */
1858 var_t *var, var_parse_t *ctx,
1859 const char *var_ptr, int var_len, int var_inc, int var_idx,
1860 const char **val_ptr, int *val_len, int *val_size)
1865 /* pass through to original callback */
1866 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1867 var_ptr, var_len, var_inc, var_idx,
1868 val_ptr, val_len, val_size);
1870 /* convert undefined variable into empty variable if relative
1871 lookups are counted. This is the case inside an active loop
1872 construct if no limits are given. There the parse_input()
1873 has to proceed until all variables have undefined values.
1874 This trick here allows it to determine this case. */
1875 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1876 ctx->rel_lookup_cnt--;
1887 /* parse complex variable construct ("${name...}") */
1889 parse_variable_complex(
1890 var_t *var, var_parse_t *ctx,
1891 const char *begin, const char *end,
1896 int len, buffer_size;
1904 /* initializations */
1906 tokenbuf_init(&name);
1907 tokenbuf_init(&tmp);
1908 tokenbuf_init(result);
1910 /* parse open delimiter */
1911 if (p == end || *p != var->syntax.delim_open)
1915 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1917 /* parse name of variable to expand. The name may consist of an
1918 arbitrary number of variable name character and contained variable
1921 /* parse a variable name */
1922 rc = parse_name(var, ctx, p, end);
1926 if (!tokenbuf_append(&name, p, rc)) {
1927 rc = VAR_ERR_OUT_OF_MEMORY;
1933 /* parse an (embedded) variable */
1934 rc = parse_variable(var, ctx, p, end, &tmp);
1938 if (!tokenbuf_merge(&name, &tmp)) {
1939 rc = VAR_ERR_OUT_OF_MEMORY;
1944 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1947 /* we must have the complete expanded variable name now,
1948 so make sure we really do. */
1949 if (name.begin == name.end) {
1950 if (ctx->force_expand) {
1951 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1955 /* If no force_expand is requested, we have to back-off.
1956 We're not sure whether our approach here is 100% correct,
1957 because it _could_ have side-effects according to Peter
1958 Simons, but as far as we know and tried it, it is
1959 correct. But be warned -- RSE */
1960 tokenbuf_set(result, begin - 1, p, 0);
1965 /* parse an optional index specification */
1966 if ( var->syntax.index_open != EOS
1967 && *p == var->syntax.index_open) {
1969 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1973 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1978 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1981 if (*p != var->syntax.index_close) {
1982 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1988 /* parse end of variable construct or start of post-operations */
1989 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1990 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1993 inc = (*p == '+'); /* increment variable */
1996 /* lookup the variable value now */
1998 tokenbuf_set(result, begin - 1, p, 0);
2000 rc = lookup_value(var, ctx,
2001 name.begin, name.end-name.begin, inc, idx,
2002 &data, &len, &buffer_size);
2003 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
2004 tokenbuf_init(result); /* delayed handling of undefined variable */
2005 } else if (rc < 0) {
2008 /* the preliminary result is the raw value of the variable.
2009 This may be modified by the operations that may follow. */
2010 tokenbuf_set(result, data, data + len, buffer_size);
2014 /* parse optional post-operations */
2017 tokenbuf_free(&tmp);
2018 tokenbuf_init(&tmp);
2020 while (p != end && *p == ':') {
2023 rc = parse_operation(var, ctx, p, end, result);
2025 rc = parse_operation(var, ctx, p, end, &tmp);
2032 if (p == end || *p != var->syntax.delim_close) {
2033 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2039 } else if (p[-1] == '+') {
2043 /* lazy handling of undefined variable */
2044 if (!failed && tokenbuf_isundef(result)) {
2045 if (ctx->force_expand) {
2046 rc = VAR_ERR_UNDEFINED_VARIABLE;
2049 tokenbuf_set(result, begin - 1, p, 0);
2053 /* return successfully */
2054 tokenbuf_free(&name);
2055 tokenbuf_free(&tmp);
2058 /* return with an error */
2060 tokenbuf_free(&name);
2061 tokenbuf_free(&tmp);
2062 tokenbuf_free(result);
2066 /* parse variable construct ("$name" or "${name...}") */
2069 var_t *var, var_parse_t *ctx,
2070 const char *begin, const char *end,
2075 int len, buffer_size;
2079 /* initialization */
2081 tokenbuf_init(result);
2083 /* parse init delimiter */
2084 if (p == end || *p != var->syntax.delim_init)
2088 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2090 /* parse a simple variable name.
2091 (if this fails, we're try to parse a complex variable construct) */
2092 rc = parse_name(var, ctx, p, end);
2096 inc = (p[rc] == '+');
2097 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2098 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2099 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2104 tokenbuf_set(result, data, data + len, buffer_size);
2108 /* parse a complex variable construct (else case) */
2109 rc = parse_variable_complex(var, ctx, p, end, result);
2115 /* parse loop construct limits ("[...]{b,s,e}") */
2118 var_t *var, var_parse_t *ctx,
2119 const char *begin, const char *end,
2120 int *start, int *step, int *stop, int *open_stop)
2126 /* initialization */
2129 /* we are happy if nothing is to left to parse */
2133 /* parse start delimiter */
2134 if (*p != var->syntax.delim_open)
2138 /* parse loop start value */
2140 rc = parse_numexp(var, ctx, p, end, start, &failed);
2141 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2142 *start = 0; /* use default */
2144 return (var_rc_t)rc;
2148 return VAR_ERR_UNDEFINED_VARIABLE;
2150 /* parse separator */
2152 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2155 /* parse loop step value */
2157 rc = parse_numexp(var, ctx, p, end, step, &failed);
2158 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2159 *step = 1; /* use default */
2161 return (var_rc_t)rc;
2165 return VAR_ERR_UNDEFINED_VARIABLE;
2167 /* parse separator */
2169 /* if not found, parse end delimiter */
2170 if (*p != var->syntax.delim_close)
2171 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2174 /* shift step value to stop value */
2178 /* determine whether loop end is open */
2183 return (var_rc_t)(p - begin);
2187 /* parse loop stop value */
2189 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2190 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2191 *stop = 0; /* use default */
2195 return (var_rc_t)rc;
2201 return VAR_ERR_UNDEFINED_VARIABLE;
2203 /* parse end delimiter */
2204 if (*p != var->syntax.delim_close)
2205 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2208 /* return amount of parsed input */
2209 return (var_rc_t)(p - begin);
2212 /* parse plain text */
2215 var_t *var, var_parse_t *ctx,
2216 const char *begin, const char *end)
2220 /* parse until delim_init (variable construct)
2221 or index_open (loop construct) is found */
2222 for (p = begin; p != end; p++) {
2223 if (*p == var->syntax.escape) {
2224 p++; /* skip next character */
2226 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2228 else if (*p == var->syntax.delim_init)
2230 else if ( var->syntax.index_open != EOS
2231 && ( *p == var->syntax.index_open
2232 || *p == var->syntax.index_close))
2238 /* expand input in general */
2241 var_t *var, var_parse_t *ctx,
2242 const char *begin, const char *end,
2243 tokenbuf_t *output, int recursion_level)
2248 int start, step, stop, open_stop;
2252 int loop_limit_length;
2255 /* initialization */
2259 /* try to parse a loop construct */
2261 && var->syntax.index_open != EOS
2262 && *p == var->syntax.index_open) {
2265 /* loop preparation */
2266 loop_limit_length = -1;
2267 rel_lookup_cnt = ctx->rel_lookup_cnt;
2275 /* iterate over loop construct, either as long as there is
2276 (still) nothing known about the limit, or there is an open
2277 (=unknown) limit stop and there are still defined variables
2278 or there is a stop limit known and it is still not reached */
2282 && ( loop_limit_length < 0
2283 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2288 /* remember current output end for restoring */
2289 output_backup = (output->end - output->begin);
2291 /* open temporary context for recursion */
2292 ctx = var_parse_push(ctx, &myctx);
2293 ctx->force_expand = 1;
2294 ctx->rel_lookup_flag = 1;
2295 ctx->index_this = i;
2297 /* recursive parse input through ourself */
2298 rc = parse_input(var, ctx, p, end,
2299 output, recursion_level+1);
2301 /* retrieve info and close temporary context */
2302 rel_lookup_cnt = ctx->rel_lookup_cnt;
2303 ctx = var_parse_pop(ctx);
2305 /* error handling */
2309 /* make sure the loop construct is closed */
2310 if (p[rc] != var->syntax.index_close) {
2311 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2315 /* try to parse loop construct limit specification */
2316 if (loop_limit_length < 0) {
2317 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2318 &start, &step, &stop, &open_stop);
2322 loop_limit_length = 0;
2324 loop_limit_length = rc2;
2325 /* restart loop from scratch */
2326 output->end = (output->begin + output_backup);
2332 /* if stop value is open, restore to the output end
2333 because the last iteration was just to determine the loop
2334 termination and its result has to be discarded */
2336 output->end = (output->begin + output_backup);
2338 /* skip parsed loop construct */
2341 p += loop_limit_length;
2346 /* try to parse plain text */
2347 rc = parse_text(var, ctx, p, end);
2349 if (!tokenbuf_append(output, p, rc)) {
2350 rc = VAR_ERR_OUT_OF_MEMORY;
2358 /* try to parse a variable construct */
2359 tokenbuf_init(&result);
2360 rc = parse_variable(var, ctx, p, end, &result);
2362 if (!tokenbuf_merge(output, &result)) {
2363 tokenbuf_free(&result);
2364 rc = VAR_ERR_OUT_OF_MEMORY;
2367 tokenbuf_free(&result);
2371 tokenbuf_free(&result);
2375 } while (p != end && rc > 0);
2377 /* We do not know whether this really could happen, but because we
2378 are paranoid, report an error at the outer most parsing level if
2379 there is still any input. Because this would mean that we are no
2380 longer able to parse the remaining input as a loop construct, a
2381 text or a variable construct. This would be very strange, but
2382 could perhaps happen in case of configuration errors!?... */
2383 if (recursion_level == 0 && p != end) {
2384 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2388 /* return amount of parsed text */
2389 return (var_rc_t)(p - begin);
2391 /* return with an error where as a special case the output begin is
2392 set to the input begin and the output end to the last input parsing
2395 tokenbuf_free(output);
2396 tokenbuf_set(output, begin, p, 0);
2397 return (var_rc_t)rc;
2402 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2406 /* create variable expansion context */
2414 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2415 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2416 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2417 memset(var, 0, sizeof(var));
2418 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2423 /* destroy variable expansion context */
2429 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2434 /* configure variable expansion context */
2445 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2448 case VAR_CONFIG_SYNTAX: {
2450 s = (var_syntax_t *)va_arg(ap, void *);
2452 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2453 var->syntax.escape = s->escape;
2454 var->syntax.delim_init = s->delim_init;
2455 var->syntax.delim_open = s->delim_open;
2456 var->syntax.delim_close = s->delim_close;
2457 var->syntax.index_open = s->index_open;
2458 var->syntax.index_close = s->index_close;
2459 var->syntax.index_mark = s->index_mark;
2460 var->syntax.name_chars = NULL; /* unused internally */
2461 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2463 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2464 || var->syntax_nameclass[(int)var->syntax.delim_open]
2465 || var->syntax_nameclass[(int)var->syntax.delim_close]
2466 || var->syntax_nameclass[(int)var->syntax.escape])
2467 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2470 case VAR_CONFIG_CB_VALUE: {
2473 fct = (var_cb_value_t)va_arg(ap, void *);
2474 ctx = (void *)va_arg(ap, void *);
2475 var->cb_value_fct = fct;
2476 var->cb_value_ctx = ctx;
2479 case VAR_CONFIG_CB_OPERATION: {
2480 var_cb_operation_t fct;
2482 fct = (var_cb_operation_t)va_arg(ap, void *);
2483 ctx = (void *)va_arg(ap, void *);
2484 var->cb_operation_fct = fct;
2485 var->cb_operation_ctx = ctx;
2489 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2495 /* perform unescape operation on a buffer */
2499 const char *src, int srclen,
2500 char *dst, int dstlen,
2506 if (var == NULL || src == NULL || dst == NULL)
2507 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2512 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2531 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2534 case '0': case '1': case '2': case '3': case '4':
2535 case '5': case '6': case '7': case '8': case '9':
2537 && isdigit((int)src[1])
2538 && isdigit((int)src[2])) {
2539 if ((rc = expand_octal(&src, &dst, end)) != 0)
2557 /* perform expand operation on a buffer */
2561 const char *src_ptr, int src_len,
2562 char **dst_ptr, int *dst_len,
2569 /* argument sanity checks */
2570 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2571 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2573 /* prepare internal expansion context */
2575 ctx.force_expand = force_expand;
2576 ctx.rel_lookup_flag = 0;
2577 ctx.rel_lookup_cnt = 0;
2580 /* start the parsing */
2581 tokenbuf_init(&output);
2582 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2584 /* post-processing */
2586 /* always EOS-terminate output for convinience reasons
2587 but do not count the EOS-terminator in the length */
2588 if (!tokenbuf_append(&output, "\0", 1)) {
2589 tokenbuf_free(&output);
2590 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2594 /* provide result */
2595 *dst_ptr = (char *)output.begin;
2596 if (dst_len != NULL)
2597 *dst_len = (output.end - output.begin);
2601 /* provide result */
2602 if (dst_len != NULL)
2603 *dst_len = (output.end - output.begin);
2609 /* format and expand a string */
2613 char **dst_ptr, int force_expand,
2614 const char *fmt, va_list ap)
2620 /* argument sanity checks */
2621 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2622 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2624 /* perform formatting */
2625 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2626 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2627 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2630 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2633 /* perform expansion */
2634 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2645 /* format and expand a string */
2649 char **dst_ptr, int force_expand,
2650 const char *fmt, ...)
2655 /* argument sanity checks */
2656 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2657 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2660 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2666 /* var_rc_t to string mapping table */
2667 static const char *var_errors[] = {
2668 _("everything ok"), /* VAR_OK = 0 */
2669 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2670 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2671 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2672 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2673 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2674 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2675 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2676 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2677 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2678 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2679 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2680 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2681 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2682 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2683 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2684 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2685 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2686 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2687 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2688 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2689 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2690 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2691 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2692 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2693 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2694 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2695 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2696 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2697 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2698 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2699 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2700 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2701 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2702 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2703 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2704 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2705 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2706 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2707 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2708 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2709 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2710 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2711 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2712 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2713 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2716 /* translate a return code into its corresponding descriptive text */
2717 const char *var_strerror(var_t *var, var_rc_t rc)
2720 rc = (var_rc_t)(0 - rc);
2721 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2722 str = _("unknown error");
2724 str = (char *)var_errors[rc];