2 Bacula® - The Network Backup Solution
4 Copyright (C) 2003-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
17 ** OSSP var - Variable Expansion
18 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
19 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
20 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
22 ** This file is part of OSSP var, a variable expansion
23 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
25 ** Permission to use, copy, modify, and distribute this software for
26 ** any purpose with or without fee is hereby granted, provided that
27 ** the above copyright notice and this permission notice appear in all
30 ** For disclaimer see below.
33 * Adapted by Kern Sibbald to Bacula June 2003
37 #if defined(HAVE_PCREPOSIX)
38 # include <pcreposix.h>
39 #elif defined(HAVE_WIN32)
46 /* support for OSSP ex based exception throwing */
50 ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \
51 ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
53 #define VAR_RC(rv) (rv)
62 ** ==== INTERNAL DATA STRUCTURES ====
66 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
68 /* the external context structure */
71 char_class_t syntax_nameclass;
72 var_cb_value_t cb_value_fct;
74 var_cb_operation_t cb_operation_fct;
75 void *cb_operation_ctx;
78 /* the internal expansion context structure */
80 struct var_parse_st *lower;
86 typedef struct var_parse_st var_parse_t;
88 /* the default syntax configuration */
89 static const var_syntax_t var_syntax_default = {
93 '}', /* delim_close */
95 ']', /* index_close */
97 "a-zA-Z0-9_" /* name_chars */
102 ** ==== FORMATTING FUNCTIONS ====
106 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
109 int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx,
110 const char *format, va_list ap)
112 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
113 char ibuf[((sizeof(int)*8)/3)+10];
123 while (*format != '\0') {
124 if (*format == '%') {
133 c = (char)va_arg(ap, int);
139 if ((cp = (char *)va_arg(ap, char *)) == NULL)
145 d = (int)va_arg(ap, int);
146 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
160 if ((format = strchr(cp, '%')) == NULL)
161 format = strchr(cp, '\0');
164 /* perform output operation */
166 if ((n = output(ctx, cp, n)) == -1)
173 /* output callback function context for var_mvsnprintf() */
177 } var_mvsnprintf_cb_t;
179 /* output callback function for var_mvsnprintf() */
183 const char *buffer, int bufsize)
185 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
187 if (bufsize > ctx->buflen)
189 memcpy(ctx->bufptr, buffer, bufsize);
190 ctx->bufptr += bufsize;
191 ctx->buflen -= bufsize;
195 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
198 char *buffer, int bufsize,
199 const char *format, va_list ap)
202 var_mvsnprintf_cb_t ctx;
206 if (buffer != NULL && bufsize == 0)
209 /* just determine output length */
210 n = var_mvxprintf(NULL, NULL, format, ap);
212 /* perform real output */
214 ctx.buflen = bufsize;
215 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
216 if (n != -1 && ctx.buflen == 0)
219 *(ctx.bufptr) = '\0';
226 ** ==== PARSE CONTEXT FUNCTIONS ====
232 var_parse_t *lower, var_parse_t *upper)
236 memcpy(upper, lower, sizeof(var_parse_t));
237 upper->lower = lower;
252 ** ==== TOKEN BUFFER FUNCTIONS ====
256 #define TOKENBUF_INITIAL_BUFSIZE 64
270 buf->buffer_size = 0;
278 if (buf->begin == NULL && buf->end == NULL)
287 if (buf->begin == buf->end)
294 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
298 buf->buffer_size = buffer_size;
304 tokenbuf_t *src, tokenbuf_t *dst)
306 dst->begin = src->begin;
308 dst->buffer_size = src->buffer_size;
315 tokenbuf_t *buf, const char *data, int len)
319 if ((p = (char *)malloc(len + 1)) == NULL)
321 memcpy(p, data, len);
324 buf->buffer_size = len + 1;
325 *((char *)(buf->end)) = EOS;
331 tokenbuf_t *output, const char *data, int len)
337 /* Is the tokenbuffer initialized at all? If not, allocate a
338 standard-sized buffer to begin with. */
339 if (output->begin == NULL) {
340 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
342 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
345 /* does the token contain text, but no buffer has been allocated yet? */
346 if (output->buffer_size == 0) {
347 /* check whether data borders to output. If, we can append
348 simly by increasing the end pointer. */
349 if (output->end == data) {
353 /* ok, so copy the contents of output into an allocated buffer
354 so that we can append that way. */
355 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
357 memcpy(tmp, output->begin, output->end - output->begin);
358 output->buffer_size = output->end - output->begin;
360 output->end = tmp + output->buffer_size;
361 output->buffer_size += len + 1;
364 /* does the token fit into the current buffer? If not, realloc a
365 larger buffer that fits. */
366 if ((output->buffer_size - (output->end - output->begin)) <= len) {
367 new_size = output->buffer_size;
370 } while ((new_size - (output->end - output->begin)) <= len);
371 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
373 output->end = new_buffer + (output->end - output->begin);
374 output->begin = new_buffer;
375 output->buffer_size = new_size;
378 /* append the data at the end of the current buffer. */
380 memcpy((char *)output->end, data, len);
382 *((char *)output->end) = EOS;
388 tokenbuf_t *output, tokenbuf_t *input)
390 return tokenbuf_append(output, input->begin, input->end - input->begin);
397 if (buf->begin != NULL && buf->buffer_size > 0)
398 free((char *)buf->begin);
399 buf->begin = buf->end = NULL;
400 buf->buffer_size = 0;
406 ** ==== CHARACTER CLASS EXPANSION ====
411 expand_range(char a, char b, char_class_t chrclass)
414 chrclass[(int)a] = 1;
420 expand_character_class(const char *desc, char_class_t chrclass)
424 /* clear the class array. */
425 for (i = 0; i < 256; ++i)
428 /* walk through class description and set appropriate entries in array */
429 while (*desc != EOS) {
430 if (desc[1] == '-' && desc[2] != EOS) {
431 if (desc[0] > desc[2])
432 return VAR_ERR_INCORRECT_CLASS_SPEC;
433 expand_range(desc[0], desc[2], chrclass);
436 chrclass[(int) *desc] = 1;
445 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
453 if (c >= '0' && c <= '7')
461 const char **src, char **dst, const char *end)
466 return VAR_ERR_INCOMPLETE_OCTAL;
467 if ( !expand_isoct(**src)
468 || !expand_isoct((*src)[1])
469 || !expand_isoct((*src)[2]))
470 return VAR_ERR_INVALID_OCTAL;
474 return VAR_ERR_OCTAL_TOO_LARGE;
493 if ((c >= '0' && c <= '9') ||
494 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
502 const char **src, char **dst, const char *end)
507 return VAR_ERR_INCOMPLETE_HEX;
508 if ( !expand_ishex(**src)
509 || !expand_ishex((*src)[1]))
510 return VAR_ERR_INVALID_HEX;
512 if (**src >= '0' && **src <= '9')
514 else if (**src >= 'a' && **src <= 'f')
515 c = **src - 'a' + 10;
516 else if (**src >= 'A' && **src <= 'F')
517 c = **src - 'A' + 10;
522 if (**src >= '0' && **src <= '9')
524 else if (**src >= 'a' && **src <= 'f')
525 c += **src - 'a' + 10;
526 else if (**src >= 'A' && **src <= 'F')
527 c += **src - 'A' + 10;
536 const char **src, char **dst, const char *end)
540 while (*src < end && **src != '}') {
541 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
546 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
553 const char **src, char **dst, const char *end)
556 return VAR_ERR_INCOMPLETE_HEX;
559 return expand_grouped_hex(src, dst, end);
561 return expand_simple_hex(src, dst, end);
566 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
570 /* forward declarations */
571 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
572 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
573 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
575 /* parse pattern text */
578 var_t *var, var_parse_t *ctx,
579 const char *begin, const char *end)
583 /* parse until '/' */
584 for (p = begin; p != end && *p != '/'; p++) {
585 if (*p == var->syntax.escape) {
587 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
594 /* parse substitution text */
597 var_t *var, var_parse_t *ctx,
598 const char *begin, const char *end)
602 /* parse until delim_init or '/' */
603 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
604 if (*p == var->syntax.escape) {
606 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
613 /* parse expression text */
616 var_t *var, var_parse_t *ctx,
617 const char *begin, const char *end)
621 /* parse until delim_init or delim_close or ':' */
622 for (p = begin; p != end
623 && *p != var->syntax.delim_init
624 && *p != var->syntax.delim_close
626 if (*p == var->syntax.escape) {
628 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
635 /* parse opertion argument text */
638 var_t *var, var_parse_t *ctx,
639 const char *begin, const char *end)
643 /* parse until delim_init or ')' */
644 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
645 if (*p == var->syntax.escape) {
647 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
655 parse_opargtext_or_variable(
656 var_t *var, var_parse_t *ctx,
657 const char *begin, const char *end,
664 tokenbuf_init(result);
670 rc = parse_opargtext(var, ctx, p, end);
674 if (!tokenbuf_append(result, p, rc)) {
675 rc = VAR_ERR_OUT_OF_MEMORY;
680 rc = parse_variable(var, ctx, p, end, &tmp);
685 if (!tokenbuf_merge(result, &tmp)) {
686 rc = VAR_ERR_OUT_OF_MEMORY;
690 tokenbuf_free(&tmp); /* KES 11/9/2003 */
697 tokenbuf_free(result);
701 /* parse expression or variable */
703 parse_exptext_or_variable(
704 var_t *var, var_parse_t *ctx,
705 const char *begin, const char *end,
708 const char *p = begin;
712 tokenbuf_init(result);
717 /* try to parse expression text */
718 rc = parse_exptext(var, ctx, p, end);
722 if (!tokenbuf_append(result, p, rc)) {
723 rc = VAR_ERR_OUT_OF_MEMORY;
729 /* try to parse variable construct */
730 rc = parse_variable(var, ctx, p, end, &tmp);
735 if (!tokenbuf_merge(result, &tmp)) {
736 rc = VAR_ERR_OUT_OF_MEMORY;
740 tokenbuf_free(&tmp); /* KES 11/9/2003 */
748 tokenbuf_free(result);
752 /* parse substitution text or variable */
754 parse_substext_or_variable(
755 var_t *var, var_parse_t *ctx,
756 const char *begin, const char *end,
759 const char *p = begin;
763 tokenbuf_init(result);
768 /* try to parse substitution text */
769 rc = parse_substext(var, ctx, p, end);
773 if (!tokenbuf_append(result, p, rc)) {
774 rc = VAR_ERR_OUT_OF_MEMORY;
780 /* try to parse substitution text */
781 rc = parse_variable(var, ctx, p, end, &tmp);
786 if (!tokenbuf_merge(result, &tmp)) {
787 rc = VAR_ERR_OUT_OF_MEMORY;
791 tokenbuf_free(&tmp); /* KES 11/9/2003 */
799 tokenbuf_free(result);
803 /* parse class description */
805 parse_class_description(
806 var_t *var, var_parse_t *ctx,
807 tokenbuf_t *src, tokenbuf_t *dst)
813 while (p != src->end) {
814 if ((src->end - p) >= 3 && p[1] == '-') {
816 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
817 for (c = *p, d = p[2]; c <= d; ++c) {
818 if (!tokenbuf_append(dst, (char *)&c, 1))
819 return VAR_ERR_OUT_OF_MEMORY;
823 if (!tokenbuf_append(dst, p, 1))
824 return VAR_ERR_OUT_OF_MEMORY;
831 /* parse regex replace part */
834 var_t *var, var_parse_t *ctx,
838 tokenbuf_t *expanded)
844 tokenbuf_init(expanded);
846 while (p != orig->end) {
848 if (orig->end - p <= 1) {
849 tokenbuf_free(expanded);
850 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
854 if (!tokenbuf_append(expanded, p, 1)) {
855 tokenbuf_free(expanded);
856 return VAR_ERR_OUT_OF_MEMORY;
861 if (!isdigit((int)*p)) {
862 tokenbuf_free(expanded);
863 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
867 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
868 tokenbuf_free(expanded);
869 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
871 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
872 pmatch[i].rm_eo - pmatch[i].rm_so)) {
873 tokenbuf_free(expanded);
874 return VAR_ERR_OUT_OF_MEMORY;
877 if (!tokenbuf_append(expanded, p, 1)) {
878 tokenbuf_free(expanded);
879 return VAR_ERR_OUT_OF_MEMORY;
888 /* operation: transpose */
891 var_t *var, var_parse_t *ctx,
896 tokenbuf_t srcclass, dstclass;
901 tokenbuf_init(&srcclass);
902 tokenbuf_init(&dstclass);
903 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
905 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
907 if (srcclass.begin == srcclass.end) {
908 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
911 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
912 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
915 if (data->buffer_size == 0) {
917 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
918 rc = VAR_ERR_OUT_OF_MEMORY;
921 tokenbuf_move(&tmp, data);
923 for (p = data->begin; p != data->end; ++p) {
924 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
925 if (*p == srcclass.begin[i]) {
926 *((char *)p) = dstclass.begin[i];
931 tokenbuf_free(&srcclass);
932 tokenbuf_free(&dstclass);
936 tokenbuf_free(search);
937 tokenbuf_free(replace);
938 tokenbuf_free(&srcclass);
939 tokenbuf_free(&dstclass);
943 /* operation: search & replace */
945 op_search_and_replace(
946 var_t *var, var_parse_t *ctx,
954 int case_insensitive = 0;
960 if (search->begin == search->end)
961 return VAR_ERR_EMPTY_SEARCH_STRING;
963 for (p = flags->begin; p != flags->end; p++) {
964 switch (tolower(*p)) {
969 case_insensitive = 1;
978 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
983 /* plain text pattern based operation */
985 for (p = data->begin; p != data->end;) {
986 if (case_insensitive)
987 rc = strncasecmp(p, search->begin, search->end - search->begin);
989 rc = strncmp(p, search->begin, search->end - search->begin);
991 /* not matched, copy character */
992 if (!tokenbuf_append(&tmp, p, 1)) {
994 return VAR_ERR_OUT_OF_MEMORY;
998 /* matched, copy replacement string */
999 tokenbuf_merge(&tmp, replace);
1000 p += (search->end - search->begin);
1002 /* append remaining text */
1003 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1004 tokenbuf_free(&tmp);
1005 return VAR_ERR_OUT_OF_MEMORY;
1011 tokenbuf_free(data);
1012 tokenbuf_move(&tmp, data);
1014 /* regular expression pattern based operation */
1016 tokenbuf_t myreplace;
1018 regmatch_t pmatch[10];
1021 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1022 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1023 return VAR_ERR_OUT_OF_MEMORY;
1024 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1025 tokenbuf_free(&tmp);
1026 return VAR_ERR_OUT_OF_MEMORY;
1029 /* compile the pattern. */
1030 rc = regcomp(&preg, tmp.begin,
1032 | (multiline ? REG_NEWLINE : 0)
1033 | (case_insensitive ? REG_ICASE : 0)));
1034 tokenbuf_free(&tmp);
1036 tokenbuf_free(&mydata);
1037 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1040 /* match the pattern and create the result string in the tmp buffer */
1041 tokenbuf_append(&tmp, "", 0);
1042 for (p = mydata.begin; p < mydata.end; ) {
1043 if (p == mydata.begin || p[-1] == '\n')
1046 regexec_flag = REG_NOTBOL;
1047 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1049 /* no (more) matching */
1050 tokenbuf_append(&tmp, p, mydata.end - p);
1054 && (p + pmatch[0].rm_so) == mydata.end
1055 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1056 /* special case: found empty pattern (usually /^/ or /$/ only)
1057 in multi-line at end of data (after the last newline) */
1058 tokenbuf_append(&tmp, p, mydata.end - p);
1062 /* append prolog string */
1063 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1065 tokenbuf_free(&tmp);
1066 tokenbuf_free(&mydata);
1067 return VAR_ERR_OUT_OF_MEMORY;
1069 /* create replace string */
1070 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1073 tokenbuf_free(&tmp);
1074 tokenbuf_free(&mydata);
1077 /* append replace string */
1078 if (!tokenbuf_merge(&tmp, &myreplace)) {
1080 tokenbuf_free(&tmp);
1081 tokenbuf_free(&mydata);
1082 tokenbuf_free(&myreplace);
1083 return VAR_ERR_OUT_OF_MEMORY;
1085 tokenbuf_free(&myreplace);
1086 /* skip now processed data */
1087 p += pmatch[0].rm_eo;
1088 /* if pattern matched an empty part (think about
1089 anchor-only regular expressions like /^/ or /$/) we
1090 skip the next character to make sure we do not enter
1091 an infinitive loop in matching */
1092 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1093 if (p >= mydata.end)
1095 if (!tokenbuf_append(&tmp, p, 1)) {
1097 tokenbuf_free(&tmp);
1098 tokenbuf_free(&mydata);
1099 return VAR_ERR_OUT_OF_MEMORY;
1103 /* append prolog string and stop processing if we
1104 do not perform the search & replace globally */
1106 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1108 tokenbuf_free(&tmp);
1109 tokenbuf_free(&mydata);
1110 return VAR_ERR_OUT_OF_MEMORY;
1117 tokenbuf_free(data);
1118 tokenbuf_move(&tmp, data);
1119 tokenbuf_free(&mydata);
1125 /* operation: offset substring */
1128 var_t *var, var_parse_t *ctx,
1137 /* determine begin of result string */
1138 if ((data->end - data->begin) < num1)
1139 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1140 p = data->begin + num1;
1142 /* if num2 is zero, we copy the rest from there. */
1144 if (!tokenbuf_assign(&res, p, data->end - p))
1145 return VAR_ERR_OUT_OF_MEMORY;
1147 /* ok, then use num2. */
1149 if ((p + num2) > data->end)
1150 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1151 if (!tokenbuf_assign(&res, p, num2))
1152 return VAR_ERR_OUT_OF_MEMORY;
1155 return VAR_ERR_OFFSET_LOGIC;
1156 if ((data->begin + num2) > data->end)
1157 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1158 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1159 return VAR_ERR_OUT_OF_MEMORY;
1162 tokenbuf_free(data);
1163 tokenbuf_move(&res, data);
1167 /* operation: padding */
1170 var_t *var, var_parse_t *ctx,
1179 if (fill->begin == fill->end)
1180 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1181 tokenbuf_init(&result);
1182 if (position == 'l') {
1184 i = width - (data->end - data->begin);
1186 i = i / (fill->end - fill->begin);
1188 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1189 return VAR_ERR_OUT_OF_MEMORY;
1192 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1193 if (!tokenbuf_append(data, fill->begin, i))
1194 return VAR_ERR_OUT_OF_MEMORY;
1196 } else if (position == 'r') {
1198 i = width - (data->end - data->begin);
1200 i = i / (fill->end - fill->begin);
1202 if (!tokenbuf_merge(&result, fill)) {
1203 tokenbuf_free(&result);
1204 return VAR_ERR_OUT_OF_MEMORY;
1208 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1209 if (!tokenbuf_append(&result, fill->begin, i)) {
1210 tokenbuf_free(&result);
1211 return VAR_ERR_OUT_OF_MEMORY;
1213 if (!tokenbuf_merge(&result, data)) {
1214 tokenbuf_free(&result);
1215 return VAR_ERR_OUT_OF_MEMORY;
1217 /* move string from temporary buffer to data buffer */
1218 tokenbuf_free(data);
1219 tokenbuf_move(&result, data);
1221 } else if (position == 'c') {
1222 /* centered padding */
1223 i = (width - (data->end - data->begin)) / 2;
1225 /* create the prefix */
1226 i = i / (fill->end - fill->begin);
1228 if (!tokenbuf_merge(&result, fill)) {
1229 tokenbuf_free(&result);
1230 return VAR_ERR_OUT_OF_MEMORY;
1234 i = ((width - (data->end - data->begin)) / 2)
1235 % (fill->end - fill->begin);
1236 if (!tokenbuf_append(&result, fill->begin, i)) {
1237 tokenbuf_free(&result);
1238 return VAR_ERR_OUT_OF_MEMORY;
1240 /* append the actual data string */
1241 if (!tokenbuf_merge(&result, data)) {
1242 tokenbuf_free(&result);
1243 return VAR_ERR_OUT_OF_MEMORY;
1245 /* append the suffix */
1246 i = width - (result.end - result.begin);
1247 i = i / (fill->end - fill->begin);
1249 if (!tokenbuf_merge(&result, fill)) {
1250 tokenbuf_free(&result);
1251 return VAR_ERR_OUT_OF_MEMORY;
1255 i = width - (result.end - result.begin);
1256 if (!tokenbuf_append(&result, fill->begin, i)) {
1257 tokenbuf_free(&result);
1258 return VAR_ERR_OUT_OF_MEMORY;
1260 /* move string from temporary buffer to data buffer */
1261 tokenbuf_free(data);
1262 tokenbuf_move(&result, data);
1268 /* parse an integer number ("123") */
1271 var_t *var, var_parse_t *ctx,
1272 const char *begin, const char *end,
1280 while (isdigit(*p) && p != end) {
1290 /* parse an operation (":x...") */
1293 var_t *var, var_parse_t *ctx,
1294 const char *begin, const char *end,
1298 tokenbuf_t tmptokbuf;
1299 tokenbuf_t search, replace, flags;
1300 tokenbuf_t number1, number2;
1306 /* initialization */
1307 tokenbuf_init(&tmptokbuf);
1308 tokenbuf_init(&search);
1309 tokenbuf_init(&replace);
1310 tokenbuf_init(&flags);
1311 tokenbuf_init(&number1);
1312 tokenbuf_init(&number2);
1317 /* dispatch through the first operation character */
1318 switch (tolower(*p)) {
1320 /* turn value to lowercase. */
1321 if (data->begin != NULL) {
1322 /* if the buffer does not live in an allocated buffer,
1323 we have to copy it before modifying the contents. */
1324 if (data->buffer_size == 0) {
1325 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1326 rc = VAR_ERR_OUT_OF_MEMORY;
1331 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1332 *ptr = (char)tolower((int)(*ptr));
1338 /* turn value to uppercase. */
1339 if (data->begin != NULL) {
1340 /* if the buffer does not live in an allocated buffer,
1341 we have to copy it before modifying the contents. */
1342 if (data->buffer_size == 0) {
1343 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1344 rc = VAR_ERR_OUT_OF_MEMORY;
1349 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1350 *ptr = (char)toupper((int)(*ptr));
1356 /* cut out substring of value. */
1358 rc = parse_integer(var, ctx, p, end, &num1);
1360 rc = VAR_ERR_MISSING_START_OFFSET;
1369 } else if (*p == '-') {
1373 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1376 rc = parse_integer(var, ctx, p, end, &num2);
1378 if (data->begin != NULL) {
1379 rc = op_offset(var, ctx, data, num1, num2, isrange);
1386 /* determine length of the value */
1387 if (data->begin != NULL) {
1388 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1389 sprintf(buf, "%d", (int)(data->end - data->begin));
1390 tokenbuf_free(data);
1391 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1392 rc = VAR_ERR_OUT_OF_MEMORY;
1400 /* substitute parameter if data is empty */
1402 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1406 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1410 if (tokenbuf_isundef(data))
1411 tokenbuf_move(&tmptokbuf, data);
1412 else if (tokenbuf_isempty(data)) {
1413 tokenbuf_free(data);
1414 tokenbuf_move(&tmptokbuf, data);
1419 /* substitute empty string if data is not empty, parameter otherwise. */
1421 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1425 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1429 if (data->begin != NULL) {
1430 if (data->begin == data->end) {
1431 tokenbuf_free(data);
1432 tokenbuf_move(&tmptokbuf, data);
1434 tokenbuf_free(data);
1435 data->begin = data->end = "";
1436 data->buffer_size = 0;
1442 /* substitute parameter if data is not empty. */
1444 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1448 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1452 if (data->begin != NULL && data->begin != data->end) {
1453 tokenbuf_free(data);
1454 tokenbuf_move(&tmptokbuf, data);
1459 /* search and replace. */
1462 return VAR_ERR_MALFORMATTED_REPLACE;
1464 rc = parse_pattern(var, ctx, p, end);
1467 tokenbuf_set(&search, p, p + rc, 0);
1470 rc = VAR_ERR_MALFORMATTED_REPLACE;
1474 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1479 rc = VAR_ERR_MALFORMATTED_REPLACE;
1483 rc = parse_exptext(var, ctx, p, end);
1486 tokenbuf_set(&flags, p, p + rc, 0);
1488 if (data->begin != NULL) {
1489 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1496 /* transpose characters from class A to class B. */
1499 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1501 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1506 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1510 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1515 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1520 rc = op_transpose(var, ctx, data, &search, &replace);
1530 return VAR_ERR_MALFORMATTED_PADDING;
1532 rc = parse_integer(var, ctx, p, end, &num1);
1534 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1539 rc = VAR_ERR_MALFORMATTED_PADDING;
1543 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1548 rc = VAR_ERR_MALFORMATTED_PADDING;
1552 if (*p != 'l' && *p != 'c' && *p != 'r') {
1553 rc = VAR_ERR_MALFORMATTED_PADDING;
1558 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1565 /* operation callback function */
1568 const char *arg_ptr;
1570 const char *val_ptr;
1572 const char *out_ptr;
1578 rc = parse_name(var, ctx, p, end);
1586 tokenbuf_init(&args);
1587 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1591 arg_ptr = args.begin;
1592 arg_len = args.end - args.begin;
1594 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1603 val_ptr = data->begin;
1604 val_len = data->end - data->begin;
1606 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1607 /* call operation callback function */
1608 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1612 &out_ptr, &out_len, &out_size);
1614 if (arg_ptr != NULL)
1615 free((void *)arg_ptr);
1618 tokenbuf_free(data);
1619 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1621 if (arg_ptr != NULL)
1622 free((void *)arg_ptr);
1626 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1629 /* return successfully */
1630 tokenbuf_free(&tmptokbuf);
1631 tokenbuf_free(&search);
1632 tokenbuf_free(&replace);
1633 tokenbuf_free(&flags);
1634 tokenbuf_free(&number1);
1635 tokenbuf_free(&number2);
1638 /* return with an error */
1640 tokenbuf_free(data);
1641 tokenbuf_free(&tmptokbuf);
1642 tokenbuf_free(&search);
1643 tokenbuf_free(&replace);
1644 tokenbuf_free(&flags);
1645 tokenbuf_free(&number1);
1646 tokenbuf_free(&number2);
1650 /* parse numerical expression operand */
1652 parse_numexp_operand(
1653 var_t *var, var_parse_t *ctx,
1654 const char *begin, const char *end,
1655 int *result, int *failed)
1662 /* initialization */
1664 tokenbuf_init(&tmp);
1666 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1668 /* parse opening numerical expression */
1670 /* parse inner numerical expression */
1671 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1676 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1677 /* parse closing parenthesis */
1679 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1682 /* parse contained variable */
1683 else if (*p == var->syntax.delim_init) {
1684 /* parse variable with forced expansion */
1685 ctx = var_parse_push(ctx, &myctx);
1686 ctx->force_expand = 1;
1687 rc = parse_variable(var, ctx, p, end, &tmp);
1688 ctx = var_parse_pop(ctx);
1690 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1692 /* parse variable without forced expansion */
1693 ctx = var_parse_push(ctx, &myctx);
1694 ctx->force_expand = 0;
1695 rc = parse_variable(var, ctx, p, end, &tmp);
1696 ctx = var_parse_pop(ctx);
1701 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1702 } else if (rc < 0) {
1706 /* parse remaining numerical expression */
1707 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1708 tokenbuf_free(&tmp);
1713 /* parse relative index mark ("#") */
1714 else if ( var->syntax.index_mark != EOS
1715 && *p == var->syntax.index_mark) {
1717 *result = ctx->index_this;
1718 if (ctx->rel_lookup_flag)
1719 ctx->rel_lookup_cnt++;
1721 /* parse plain integer number */
1722 else if (isdigit(*p)) {
1723 rc = parse_integer(var, ctx, p, end, result);
1726 /* parse signed positive integer number */
1727 else if (*p == '+') {
1728 if ((end - p) > 1 && isdigit(p[1])) {
1730 rc = parse_integer(var, ctx, p, end, result);
1734 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1736 /* parse signed negative integer number */
1737 else if (*p == '-') {
1738 if (end - p > 1 && isdigit(p[1])) {
1740 rc = parse_integer(var, ctx, p, end, result);
1741 *result = -(*result);
1745 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1747 /* else we failed to parse anything reasonable */
1749 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1754 /* parse numerical expression ("x+y") */
1757 var_t *var, var_parse_t *ctx,
1758 const char *begin, const char *end,
1759 int *result, int *failed)
1766 /* initialization */
1769 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1771 /* parse left numerical operand */
1772 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1777 /* parse numerical operator */
1779 if (*p == '+' || *p == '-') {
1781 /* recursively parse right operand (light binding) */
1782 rc = parse_numexp(var, ctx, p, end, &right, failed);
1787 *result = (*result + right);
1789 *result = (*result - right);
1791 else if (*p == '*' || *p == '/' || *p == '%') {
1793 /* recursively parse right operand (string binding) */
1794 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1799 *result = (*result * right);
1800 else if (op == '/') {
1805 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1808 *result = (*result / right);
1810 else if (op == '%') {
1815 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1818 *result = (*result % right);
1825 /* return amount of parsed input */
1829 /* parse variable name ("abc") */
1832 var_t *var, var_parse_t *ctx,
1833 const char *begin, const char *end)
1837 /* parse as long as name class characters are found */
1838 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1843 /* lookup a variable value through the callback function */
1846 var_t *var, var_parse_t *ctx,
1847 const char *var_ptr, int var_len, int var_inc, int var_idx,
1848 const char **val_ptr, int *val_len, int *val_size)
1853 /* pass through to original callback */
1854 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1855 var_ptr, var_len, var_inc, var_idx,
1856 val_ptr, val_len, val_size);
1858 /* convert undefined variable into empty variable if relative
1859 lookups are counted. This is the case inside an active loop
1860 construct if no limits are given. There the parse_input()
1861 has to proceed until all variables have undefined values.
1862 This trick here allows it to determine this case. */
1863 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1864 ctx->rel_lookup_cnt--;
1875 /* parse complex variable construct ("${name...}") */
1877 parse_variable_complex(
1878 var_t *var, var_parse_t *ctx,
1879 const char *begin, const char *end,
1884 int len, buffer_size;
1892 /* initializations */
1894 tokenbuf_init(&name);
1895 tokenbuf_init(&tmp);
1896 tokenbuf_init(result);
1898 /* parse open delimiter */
1899 if (p == end || *p != var->syntax.delim_open)
1903 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1905 /* parse name of variable to expand. The name may consist of an
1906 arbitrary number of variable name character and contained variable
1909 /* parse a variable name */
1910 rc = parse_name(var, ctx, p, end);
1914 if (!tokenbuf_append(&name, p, rc)) {
1915 rc = VAR_ERR_OUT_OF_MEMORY;
1921 /* parse an (embedded) variable */
1922 rc = parse_variable(var, ctx, p, end, &tmp);
1926 if (!tokenbuf_merge(&name, &tmp)) {
1927 rc = VAR_ERR_OUT_OF_MEMORY;
1932 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1935 /* we must have the complete expanded variable name now,
1936 so make sure we really do. */
1937 if (name.begin == name.end) {
1938 if (ctx->force_expand) {
1939 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1943 /* If no force_expand is requested, we have to back-off.
1944 We're not sure whether our approach here is 100% correct,
1945 because it _could_ have side-effects according to Peter
1946 Simons, but as far as we know and tried it, it is
1947 correct. But be warned -- RSE */
1948 tokenbuf_set(result, begin - 1, p, 0);
1953 /* parse an optional index specification */
1954 if ( var->syntax.index_open != EOS
1955 && *p == var->syntax.index_open) {
1957 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1961 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1966 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1969 if (*p != var->syntax.index_close) {
1970 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1976 /* parse end of variable construct or start of post-operations */
1977 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1978 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1981 if ((inc = (*p++ == '+'))) {
1982 p++; /* skip the + */
1985 /* lookup the variable value now */
1987 tokenbuf_set(result, begin - 1, p, 0);
1989 rc = lookup_value(var, ctx,
1990 name.begin, name.end-name.begin, inc, idx,
1991 &data, &len, &buffer_size);
1992 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1993 tokenbuf_init(result); /* delayed handling of undefined variable */
1994 } else if (rc < 0) {
1997 /* the preliminary result is the raw value of the variable.
1998 This may be modified by the operations that may follow. */
1999 tokenbuf_set(result, data, data + len, buffer_size);
2003 /* parse optional post-operations */
2006 tokenbuf_free(&tmp);
2007 tokenbuf_init(&tmp);
2009 while (p != end && *p == ':') {
2012 rc = parse_operation(var, ctx, p, end, result);
2014 rc = parse_operation(var, ctx, p, end, &tmp);
2021 if (p == end || *p != var->syntax.delim_close) {
2022 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2028 } else if (p[-1] == '+') {
2032 /* lazy handling of undefined variable */
2033 if (!failed && tokenbuf_isundef(result)) {
2034 if (ctx->force_expand) {
2035 rc = VAR_ERR_UNDEFINED_VARIABLE;
2038 tokenbuf_set(result, begin - 1, p, 0);
2042 /* return successfully */
2043 tokenbuf_free(&name);
2044 tokenbuf_free(&tmp);
2047 /* return with an error */
2049 tokenbuf_free(&name);
2050 tokenbuf_free(&tmp);
2051 tokenbuf_free(result);
2055 /* parse variable construct ("$name" or "${name...}") */
2058 var_t *var, var_parse_t *ctx,
2059 const char *begin, const char *end,
2064 int len, buffer_size;
2068 /* initialization */
2070 tokenbuf_init(result);
2072 /* parse init delimiter */
2073 if (p == end || *p != var->syntax.delim_init)
2077 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2079 /* parse a simple variable name.
2080 (if this fails, we're try to parse a complex variable construct) */
2081 rc = parse_name(var, ctx, p, end);
2085 inc = (p[rc] == '+');
2086 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2087 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2088 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2093 tokenbuf_set(result, data, data + len, buffer_size);
2097 /* parse a complex variable construct (else case) */
2098 rc = parse_variable_complex(var, ctx, p, end, result);
2104 /* parse loop construct limits ("[...]{b,s,e}") */
2107 var_t *var, var_parse_t *ctx,
2108 const char *begin, const char *end,
2109 int *start, int *step, int *stop, int *open_stop)
2115 /* initialization */
2118 /* we are happy if nothing is to left to parse */
2122 /* parse start delimiter */
2123 if (*p != var->syntax.delim_open)
2127 /* parse loop start value */
2129 rc = parse_numexp(var, ctx, p, end, start, &failed);
2130 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2131 *start = 0; /* use default */
2133 return (var_rc_t)rc;
2137 return VAR_ERR_UNDEFINED_VARIABLE;
2139 /* parse separator */
2141 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2144 /* parse loop step value */
2146 rc = parse_numexp(var, ctx, p, end, step, &failed);
2147 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2148 *step = 1; /* use default */
2150 return (var_rc_t)rc;
2154 return VAR_ERR_UNDEFINED_VARIABLE;
2156 /* parse separator */
2158 /* if not found, parse end delimiter */
2159 if (*p != var->syntax.delim_close)
2160 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2163 /* shift step value to stop value */
2167 /* determine whether loop end is open */
2172 return (var_rc_t)(p - begin);
2176 /* parse loop stop value */
2178 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2179 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2180 *stop = 0; /* use default */
2184 return (var_rc_t)rc;
2190 return VAR_ERR_UNDEFINED_VARIABLE;
2192 /* parse end delimiter */
2193 if (*p != var->syntax.delim_close)
2194 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2197 /* return amount of parsed input */
2198 return (var_rc_t)(p - begin);
2201 /* parse plain text */
2204 var_t *var, var_parse_t *ctx,
2205 const char *begin, const char *end)
2209 /* parse until delim_init (variable construct)
2210 or index_open (loop construct) is found */
2211 for (p = begin; p != end; p++) {
2212 if (*p == var->syntax.escape) {
2213 p++; /* skip next character */
2215 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2217 else if (*p == var->syntax.delim_init)
2219 else if ( var->syntax.index_open != EOS
2220 && ( *p == var->syntax.index_open
2221 || *p == var->syntax.index_close))
2227 /* expand input in general */
2230 var_t *var, var_parse_t *ctx,
2231 const char *begin, const char *end,
2232 tokenbuf_t *output, int recursion_level)
2237 int start, step, stop, open_stop;
2241 int loop_limit_length;
2244 /* initialization */
2248 /* try to parse a loop construct */
2250 && var->syntax.index_open != EOS
2251 && *p == var->syntax.index_open) {
2254 /* loop preparation */
2255 loop_limit_length = -1;
2256 rel_lookup_cnt = ctx->rel_lookup_cnt;
2264 /* iterate over loop construct, either as long as there is
2265 (still) nothing known about the limit, or there is an open
2266 (=unknown) limit stop and there are still defined variables
2267 or there is a stop limit known and it is still not reached */
2271 && ( loop_limit_length < 0
2272 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2277 /* remember current output end for restoring */
2278 output_backup = (output->end - output->begin);
2280 /* open temporary context for recursion */
2281 ctx = var_parse_push(ctx, &myctx);
2282 ctx->force_expand = 1;
2283 ctx->rel_lookup_flag = 1;
2284 ctx->index_this = i;
2286 /* recursive parse input through ourself */
2287 rc = parse_input(var, ctx, p, end,
2288 output, recursion_level+1);
2290 /* retrieve info and close temporary context */
2291 rel_lookup_cnt = ctx->rel_lookup_cnt;
2292 ctx = var_parse_pop(ctx);
2294 /* error handling */
2298 /* make sure the loop construct is closed */
2299 if (p[rc] != var->syntax.index_close) {
2300 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2304 /* try to parse loop construct limit specification */
2305 if (loop_limit_length < 0) {
2306 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2307 &start, &step, &stop, &open_stop);
2311 loop_limit_length = 0;
2313 loop_limit_length = rc2;
2314 /* restart loop from scratch */
2315 output->end = (output->begin + output_backup);
2321 /* if stop value is open, restore to the output end
2322 because the last iteration was just to determine the loop
2323 termination and its result has to be discarded */
2325 output->end = (output->begin + output_backup);
2327 /* skip parsed loop construct */
2330 p += loop_limit_length;
2335 /* try to parse plain text */
2336 rc = parse_text(var, ctx, p, end);
2338 if (!tokenbuf_append(output, p, rc)) {
2339 rc = VAR_ERR_OUT_OF_MEMORY;
2347 /* try to parse a variable construct */
2348 tokenbuf_init(&result);
2349 rc = parse_variable(var, ctx, p, end, &result);
2351 if (!tokenbuf_merge(output, &result)) {
2352 tokenbuf_free(&result);
2353 rc = VAR_ERR_OUT_OF_MEMORY;
2356 tokenbuf_free(&result);
2360 tokenbuf_free(&result);
2364 } while (p != end && rc > 0);
2366 /* We do not know whether this really could happen, but because we
2367 are paranoid, report an error at the outer most parsing level if
2368 there is still any input. Because this would mean that we are no
2369 longer able to parse the remaining input as a loop construct, a
2370 text or a variable construct. This would be very strange, but
2371 could perhaps happen in case of configuration errors!?... */
2372 if (recursion_level == 0 && p != end) {
2373 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2377 /* return amount of parsed text */
2378 return (var_rc_t)(p - begin);
2380 /* return with an error where as a special case the output begin is
2381 set to the input begin and the output end to the last input parsing
2384 tokenbuf_free(output);
2385 tokenbuf_set(output, begin, p, 0);
2386 return (var_rc_t)rc;
2391 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2395 /* create variable expansion context */
2403 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2404 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2405 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2406 memset(var, 0, sizeof(*var));
2407 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2412 /* destroy variable expansion context */
2418 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2423 /* configure variable expansion context */
2434 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2437 case VAR_CONFIG_SYNTAX: {
2439 s = (var_syntax_t *)va_arg(ap, void *);
2441 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2442 var->syntax.escape = s->escape;
2443 var->syntax.delim_init = s->delim_init;
2444 var->syntax.delim_open = s->delim_open;
2445 var->syntax.delim_close = s->delim_close;
2446 var->syntax.index_open = s->index_open;
2447 var->syntax.index_close = s->index_close;
2448 var->syntax.index_mark = s->index_mark;
2449 var->syntax.name_chars = NULL; /* unused internally */
2450 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2452 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2453 || var->syntax_nameclass[(int)var->syntax.delim_open]
2454 || var->syntax_nameclass[(int)var->syntax.delim_close]
2455 || var->syntax_nameclass[(int)var->syntax.escape])
2456 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2459 case VAR_CONFIG_CB_VALUE: {
2462 fct = (var_cb_value_t)va_arg(ap, void *);
2463 ctx = (void *)va_arg(ap, void *);
2464 var->cb_value_fct = fct;
2465 var->cb_value_ctx = ctx;
2468 case VAR_CONFIG_CB_OPERATION: {
2469 var_cb_operation_t fct;
2471 fct = (var_cb_operation_t)va_arg(ap, void *);
2472 ctx = (void *)va_arg(ap, void *);
2473 var->cb_operation_fct = fct;
2474 var->cb_operation_ctx = ctx;
2478 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2484 /* perform unescape operation on a buffer */
2488 const char *src, int srclen,
2489 char *dst, int dstlen,
2495 if (var == NULL || src == NULL || dst == NULL)
2496 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2501 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2520 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2523 case '0': case '1': case '2': case '3': case '4':
2524 case '5': case '6': case '7': case '8': case '9':
2526 && isdigit((int)src[1])
2527 && isdigit((int)src[2])) {
2528 if ((rc = expand_octal(&src, &dst, end)) != 0)
2546 /* perform expand operation on a buffer */
2550 const char *src_ptr, int src_len,
2551 char **dst_ptr, int *dst_len,
2558 /* argument sanity checks */
2559 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2560 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2562 /* prepare internal expansion context */
2564 ctx.force_expand = force_expand;
2565 ctx.rel_lookup_flag = 0;
2566 ctx.rel_lookup_cnt = 0;
2569 /* start the parsing */
2570 tokenbuf_init(&output);
2571 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2573 /* post-processing */
2575 /* always EOS-terminate output for convinience reasons
2576 but do not count the EOS-terminator in the length */
2577 if (!tokenbuf_append(&output, "\0", 1)) {
2578 tokenbuf_free(&output);
2579 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2583 /* provide result */
2584 *dst_ptr = (char *)output.begin;
2585 if (dst_len != NULL)
2586 *dst_len = (output.end - output.begin);
2590 /* provide result */
2591 if (dst_len != NULL)
2592 *dst_len = (output.end - output.begin);
2598 /* format and expand a string */
2602 char **dst_ptr, int force_expand,
2603 const char *fmt, va_list ap)
2609 /* argument sanity checks */
2610 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2611 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2613 /* perform formatting */
2614 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2615 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2616 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2619 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2622 /* perform expansion */
2623 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2634 /* format and expand a string */
2638 char **dst_ptr, int force_expand,
2639 const char *fmt, ...)
2644 /* argument sanity checks */
2645 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2646 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2649 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2655 /* var_rc_t to string mapping table */
2656 static const char *var_errors[] = {
2657 _("everything ok"), /* VAR_OK = 0 */
2658 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2659 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2660 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2661 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2662 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2663 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2664 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2665 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2666 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2667 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2668 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2669 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2670 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2671 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2672 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2673 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2674 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2675 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2676 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2677 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2678 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2679 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2680 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2681 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2682 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2683 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2684 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2685 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2686 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2687 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2688 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2689 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2690 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2691 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2692 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2693 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2694 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2695 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2696 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2697 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2698 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2699 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2700 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2701 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2702 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2705 /* translate a return code into its corresponding descriptive text */
2706 const char *var_strerror(var_t *var, var_rc_t rc)
2709 rc = (var_rc_t)(0 - rc);
2710 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2711 str = _("unknown error");
2713 str = (char *)var_errors[rc];