2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 ** OSSP var - Variable Expansion
30 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
31 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
32 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
34 ** This file is part of OSSP var, a variable expansion
35 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
37 ** Permission to use, copy, modify, and distribute this software for
38 ** any purpose with or without fee is hereby granted, provided that
39 ** the above copyright notice and this permission notice appear in all
42 ** For disclaimer see below.
45 * Adapted by Kern Sibbald to Bacula June 2003
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 if ((inc = (*p++ == '+'))) {
1994 p++; /* skip the + */
1997 /* lookup the variable value now */
1999 tokenbuf_set(result, begin - 1, p, 0);
2001 rc = lookup_value(var, ctx,
2002 name.begin, name.end-name.begin, inc, idx,
2003 &data, &len, &buffer_size);
2004 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
2005 tokenbuf_init(result); /* delayed handling of undefined variable */
2006 } else if (rc < 0) {
2009 /* the preliminary result is the raw value of the variable.
2010 This may be modified by the operations that may follow. */
2011 tokenbuf_set(result, data, data + len, buffer_size);
2015 /* parse optional post-operations */
2018 tokenbuf_free(&tmp);
2019 tokenbuf_init(&tmp);
2021 while (p != end && *p == ':') {
2024 rc = parse_operation(var, ctx, p, end, result);
2026 rc = parse_operation(var, ctx, p, end, &tmp);
2033 if (p == end || *p != var->syntax.delim_close) {
2034 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2040 } else if (p[-1] == '+') {
2044 /* lazy handling of undefined variable */
2045 if (!failed && tokenbuf_isundef(result)) {
2046 if (ctx->force_expand) {
2047 rc = VAR_ERR_UNDEFINED_VARIABLE;
2050 tokenbuf_set(result, begin - 1, p, 0);
2054 /* return successfully */
2055 tokenbuf_free(&name);
2056 tokenbuf_free(&tmp);
2059 /* return with an error */
2061 tokenbuf_free(&name);
2062 tokenbuf_free(&tmp);
2063 tokenbuf_free(result);
2067 /* parse variable construct ("$name" or "${name...}") */
2070 var_t *var, var_parse_t *ctx,
2071 const char *begin, const char *end,
2076 int len, buffer_size;
2080 /* initialization */
2082 tokenbuf_init(result);
2084 /* parse init delimiter */
2085 if (p == end || *p != var->syntax.delim_init)
2089 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2091 /* parse a simple variable name.
2092 (if this fails, we're try to parse a complex variable construct) */
2093 rc = parse_name(var, ctx, p, end);
2097 inc = (p[rc] == '+');
2098 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2099 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2100 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2105 tokenbuf_set(result, data, data + len, buffer_size);
2109 /* parse a complex variable construct (else case) */
2110 rc = parse_variable_complex(var, ctx, p, end, result);
2116 /* parse loop construct limits ("[...]{b,s,e}") */
2119 var_t *var, var_parse_t *ctx,
2120 const char *begin, const char *end,
2121 int *start, int *step, int *stop, int *open_stop)
2127 /* initialization */
2130 /* we are happy if nothing is to left to parse */
2134 /* parse start delimiter */
2135 if (*p != var->syntax.delim_open)
2139 /* parse loop start value */
2141 rc = parse_numexp(var, ctx, p, end, start, &failed);
2142 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2143 *start = 0; /* use default */
2145 return (var_rc_t)rc;
2149 return VAR_ERR_UNDEFINED_VARIABLE;
2151 /* parse separator */
2153 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2156 /* parse loop step value */
2158 rc = parse_numexp(var, ctx, p, end, step, &failed);
2159 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2160 *step = 1; /* use default */
2162 return (var_rc_t)rc;
2166 return VAR_ERR_UNDEFINED_VARIABLE;
2168 /* parse separator */
2170 /* if not found, parse end delimiter */
2171 if (*p != var->syntax.delim_close)
2172 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2175 /* shift step value to stop value */
2179 /* determine whether loop end is open */
2184 return (var_rc_t)(p - begin);
2188 /* parse loop stop value */
2190 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2191 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2192 *stop = 0; /* use default */
2196 return (var_rc_t)rc;
2202 return VAR_ERR_UNDEFINED_VARIABLE;
2204 /* parse end delimiter */
2205 if (*p != var->syntax.delim_close)
2206 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2209 /* return amount of parsed input */
2210 return (var_rc_t)(p - begin);
2213 /* parse plain text */
2216 var_t *var, var_parse_t *ctx,
2217 const char *begin, const char *end)
2221 /* parse until delim_init (variable construct)
2222 or index_open (loop construct) is found */
2223 for (p = begin; p != end; p++) {
2224 if (*p == var->syntax.escape) {
2225 p++; /* skip next character */
2227 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2229 else if (*p == var->syntax.delim_init)
2231 else if ( var->syntax.index_open != EOS
2232 && ( *p == var->syntax.index_open
2233 || *p == var->syntax.index_close))
2239 /* expand input in general */
2242 var_t *var, var_parse_t *ctx,
2243 const char *begin, const char *end,
2244 tokenbuf_t *output, int recursion_level)
2249 int start, step, stop, open_stop;
2253 int loop_limit_length;
2256 /* initialization */
2260 /* try to parse a loop construct */
2262 && var->syntax.index_open != EOS
2263 && *p == var->syntax.index_open) {
2266 /* loop preparation */
2267 loop_limit_length = -1;
2268 rel_lookup_cnt = ctx->rel_lookup_cnt;
2276 /* iterate over loop construct, either as long as there is
2277 (still) nothing known about the limit, or there is an open
2278 (=unknown) limit stop and there are still defined variables
2279 or there is a stop limit known and it is still not reached */
2283 && ( loop_limit_length < 0
2284 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2289 /* remember current output end for restoring */
2290 output_backup = (output->end - output->begin);
2292 /* open temporary context for recursion */
2293 ctx = var_parse_push(ctx, &myctx);
2294 ctx->force_expand = 1;
2295 ctx->rel_lookup_flag = 1;
2296 ctx->index_this = i;
2298 /* recursive parse input through ourself */
2299 rc = parse_input(var, ctx, p, end,
2300 output, recursion_level+1);
2302 /* retrieve info and close temporary context */
2303 rel_lookup_cnt = ctx->rel_lookup_cnt;
2304 ctx = var_parse_pop(ctx);
2306 /* error handling */
2310 /* make sure the loop construct is closed */
2311 if (p[rc] != var->syntax.index_close) {
2312 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2316 /* try to parse loop construct limit specification */
2317 if (loop_limit_length < 0) {
2318 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2319 &start, &step, &stop, &open_stop);
2323 loop_limit_length = 0;
2325 loop_limit_length = rc2;
2326 /* restart loop from scratch */
2327 output->end = (output->begin + output_backup);
2333 /* if stop value is open, restore to the output end
2334 because the last iteration was just to determine the loop
2335 termination and its result has to be discarded */
2337 output->end = (output->begin + output_backup);
2339 /* skip parsed loop construct */
2342 p += loop_limit_length;
2347 /* try to parse plain text */
2348 rc = parse_text(var, ctx, p, end);
2350 if (!tokenbuf_append(output, p, rc)) {
2351 rc = VAR_ERR_OUT_OF_MEMORY;
2359 /* try to parse a variable construct */
2360 tokenbuf_init(&result);
2361 rc = parse_variable(var, ctx, p, end, &result);
2363 if (!tokenbuf_merge(output, &result)) {
2364 tokenbuf_free(&result);
2365 rc = VAR_ERR_OUT_OF_MEMORY;
2368 tokenbuf_free(&result);
2372 tokenbuf_free(&result);
2376 } while (p != end && rc > 0);
2378 /* We do not know whether this really could happen, but because we
2379 are paranoid, report an error at the outer most parsing level if
2380 there is still any input. Because this would mean that we are no
2381 longer able to parse the remaining input as a loop construct, a
2382 text or a variable construct. This would be very strange, but
2383 could perhaps happen in case of configuration errors!?... */
2384 if (recursion_level == 0 && p != end) {
2385 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2389 /* return amount of parsed text */
2390 return (var_rc_t)(p - begin);
2392 /* return with an error where as a special case the output begin is
2393 set to the input begin and the output end to the last input parsing
2396 tokenbuf_free(output);
2397 tokenbuf_set(output, begin, p, 0);
2398 return (var_rc_t)rc;
2403 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2407 /* create variable expansion context */
2415 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2416 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2417 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2418 memset(var, 0, sizeof(var));
2419 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2424 /* destroy variable expansion context */
2430 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2435 /* configure variable expansion context */
2446 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2449 case VAR_CONFIG_SYNTAX: {
2451 s = (var_syntax_t *)va_arg(ap, void *);
2453 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2454 var->syntax.escape = s->escape;
2455 var->syntax.delim_init = s->delim_init;
2456 var->syntax.delim_open = s->delim_open;
2457 var->syntax.delim_close = s->delim_close;
2458 var->syntax.index_open = s->index_open;
2459 var->syntax.index_close = s->index_close;
2460 var->syntax.index_mark = s->index_mark;
2461 var->syntax.name_chars = NULL; /* unused internally */
2462 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2464 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2465 || var->syntax_nameclass[(int)var->syntax.delim_open]
2466 || var->syntax_nameclass[(int)var->syntax.delim_close]
2467 || var->syntax_nameclass[(int)var->syntax.escape])
2468 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2471 case VAR_CONFIG_CB_VALUE: {
2474 fct = (var_cb_value_t)va_arg(ap, void *);
2475 ctx = (void *)va_arg(ap, void *);
2476 var->cb_value_fct = fct;
2477 var->cb_value_ctx = ctx;
2480 case VAR_CONFIG_CB_OPERATION: {
2481 var_cb_operation_t fct;
2483 fct = (var_cb_operation_t)va_arg(ap, void *);
2484 ctx = (void *)va_arg(ap, void *);
2485 var->cb_operation_fct = fct;
2486 var->cb_operation_ctx = ctx;
2490 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2496 /* perform unescape operation on a buffer */
2500 const char *src, int srclen,
2501 char *dst, int dstlen,
2507 if (var == NULL || src == NULL || dst == NULL)
2508 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2513 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2532 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2535 case '0': case '1': case '2': case '3': case '4':
2536 case '5': case '6': case '7': case '8': case '9':
2538 && isdigit((int)src[1])
2539 && isdigit((int)src[2])) {
2540 if ((rc = expand_octal(&src, &dst, end)) != 0)
2558 /* perform expand operation on a buffer */
2562 const char *src_ptr, int src_len,
2563 char **dst_ptr, int *dst_len,
2570 /* argument sanity checks */
2571 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2572 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2574 /* prepare internal expansion context */
2576 ctx.force_expand = force_expand;
2577 ctx.rel_lookup_flag = 0;
2578 ctx.rel_lookup_cnt = 0;
2581 /* start the parsing */
2582 tokenbuf_init(&output);
2583 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2585 /* post-processing */
2587 /* always EOS-terminate output for convinience reasons
2588 but do not count the EOS-terminator in the length */
2589 if (!tokenbuf_append(&output, "\0", 1)) {
2590 tokenbuf_free(&output);
2591 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2595 /* provide result */
2596 *dst_ptr = (char *)output.begin;
2597 if (dst_len != NULL)
2598 *dst_len = (output.end - output.begin);
2602 /* provide result */
2603 if (dst_len != NULL)
2604 *dst_len = (output.end - output.begin);
2610 /* format and expand a string */
2614 char **dst_ptr, int force_expand,
2615 const char *fmt, va_list ap)
2621 /* argument sanity checks */
2622 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2623 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2625 /* perform formatting */
2626 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2627 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2628 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2631 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2634 /* perform expansion */
2635 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2646 /* format and expand a string */
2650 char **dst_ptr, int force_expand,
2651 const char *fmt, ...)
2656 /* argument sanity checks */
2657 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2658 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2661 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2667 /* var_rc_t to string mapping table */
2668 static const char *var_errors[] = {
2669 _("everything ok"), /* VAR_OK = 0 */
2670 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2671 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2672 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2673 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2674 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2675 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2676 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2677 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2678 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2679 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2680 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2681 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2682 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2683 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2684 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2685 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2686 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2687 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2688 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2689 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2690 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2691 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2692 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2693 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2694 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2695 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2696 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2697 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2698 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2699 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2700 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2701 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2702 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2703 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2704 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2705 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2706 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2707 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2708 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2709 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2710 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2711 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2712 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2713 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2714 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2717 /* translate a return code into its corresponding descriptive text */
2718 const char *var_strerror(var_t *var, var_rc_t rc)
2721 rc = (var_rc_t)(0 - rc);
2722 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2723 str = _("unknown error");
2725 str = (char *)var_errors[rc];