2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many 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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 ** OSSP var - Variable Expansion
21 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
22 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
23 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
25 ** This file is part of OSSP var, a variable expansion
26 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
28 ** Permission to use, copy, modify, and distribute this software for
29 ** any purpose with or without fee is hereby granted, provided that
30 ** the above copyright notice and this permission notice appear in all
33 ** For disclaimer see below.
36 * Adapted by Kern Sibbald to Bacula June 2003
40 #if defined(HAVE_PCREPOSIX)
41 # include <pcreposix.h>
42 #elif defined(HAVE_WIN32)
49 /* support for OSSP ex based exception throwing */
53 ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \
54 ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
56 #define VAR_RC(rv) (rv)
65 ** ==== INTERNAL DATA STRUCTURES ====
69 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
71 /* the external context structure */
74 char_class_t syntax_nameclass;
75 var_cb_value_t cb_value_fct;
77 var_cb_operation_t cb_operation_fct;
78 void *cb_operation_ctx;
81 /* the internal expansion context structure */
83 struct var_parse_st *lower;
89 typedef struct var_parse_st var_parse_t;
91 /* the default syntax configuration */
92 static const var_syntax_t var_syntax_default = {
96 '}', /* delim_close */
98 ']', /* index_close */
100 "a-zA-Z0-9_" /* name_chars */
105 ** ==== FORMATTING FUNCTIONS ====
109 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
112 int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx,
113 const char *format, va_list ap)
115 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
116 char ibuf[((sizeof(int)*8)/3)+10];
126 while (*format != '\0') {
127 if (*format == '%') {
136 c = (char)va_arg(ap, int);
142 if ((cp = (char *)va_arg(ap, char *)) == NULL)
148 d = (int)va_arg(ap, int);
149 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
163 if ((format = strchr(cp, '%')) == NULL)
164 format = strchr(cp, '\0');
167 /* perform output operation */
169 if ((n = output(ctx, cp, n)) == -1)
176 /* output callback function context for var_mvsnprintf() */
180 } var_mvsnprintf_cb_t;
182 /* output callback function for var_mvsnprintf() */
186 const char *buffer, int bufsize)
188 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
190 if (bufsize > ctx->buflen)
192 memcpy(ctx->bufptr, buffer, bufsize);
193 ctx->bufptr += bufsize;
194 ctx->buflen -= bufsize;
198 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
201 char *buffer, int bufsize,
202 const char *format, va_list ap)
205 var_mvsnprintf_cb_t ctx;
209 if (buffer != NULL && bufsize == 0)
212 /* just determine output length */
213 n = var_mvxprintf(NULL, NULL, format, ap);
215 /* perform real output */
217 ctx.buflen = bufsize;
218 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
219 if (n != -1 && ctx.buflen == 0)
222 *(ctx.bufptr) = '\0';
229 ** ==== PARSE CONTEXT FUNCTIONS ====
235 var_parse_t *lower, var_parse_t *upper)
239 memcpy(upper, lower, sizeof(var_parse_t));
240 upper->lower = lower;
255 ** ==== TOKEN BUFFER FUNCTIONS ====
259 #define TOKENBUF_INITIAL_BUFSIZE 64
273 buf->buffer_size = 0;
281 if (buf->begin == NULL && buf->end == NULL)
290 if (buf->begin == buf->end)
297 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
301 buf->buffer_size = buffer_size;
307 tokenbuf_t *src, tokenbuf_t *dst)
309 dst->begin = src->begin;
311 dst->buffer_size = src->buffer_size;
318 tokenbuf_t *buf, const char *data, int len)
322 if ((p = (char *)malloc(len + 1)) == NULL)
324 memcpy(p, data, len);
327 buf->buffer_size = len + 1;
328 *((char *)(buf->end)) = EOS;
334 tokenbuf_t *output, const char *data, int len)
340 /* Is the tokenbuffer initialized at all? If not, allocate a
341 standard-sized buffer to begin with. */
342 if (output->begin == NULL) {
343 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
345 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
348 /* does the token contain text, but no buffer has been allocated yet? */
349 if (output->buffer_size == 0) {
350 /* check whether data borders to output. If, we can append
351 simly by increasing the end pointer. */
352 if (output->end == data) {
356 /* ok, so copy the contents of output into an allocated buffer
357 so that we can append that way. */
358 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
360 memcpy(tmp, output->begin, output->end - output->begin);
361 output->buffer_size = output->end - output->begin;
363 output->end = tmp + output->buffer_size;
364 output->buffer_size += len + 1;
367 /* does the token fit into the current buffer? If not, realloc a
368 larger buffer that fits. */
369 if ((output->buffer_size - (output->end - output->begin)) <= len) {
370 new_size = output->buffer_size;
373 } while ((new_size - (output->end - output->begin)) <= len);
374 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
376 output->end = new_buffer + (output->end - output->begin);
377 output->begin = new_buffer;
378 output->buffer_size = new_size;
381 /* append the data at the end of the current buffer. */
383 memcpy((char *)output->end, data, len);
385 *((char *)output->end) = EOS;
391 tokenbuf_t *output, tokenbuf_t *input)
393 return tokenbuf_append(output, input->begin, input->end - input->begin);
400 if (buf->begin != NULL && buf->buffer_size > 0)
401 free((char *)buf->begin);
402 buf->begin = buf->end = NULL;
403 buf->buffer_size = 0;
409 ** ==== CHARACTER CLASS EXPANSION ====
414 expand_range(char a, char b, char_class_t chrclass)
417 chrclass[(int)a] = 1;
423 expand_character_class(const char *desc, char_class_t chrclass)
427 /* clear the class array. */
428 for (i = 0; i < 256; ++i)
431 /* walk through class description and set appropriate entries in array */
432 while (*desc != EOS) {
433 if (desc[1] == '-' && desc[2] != EOS) {
434 if (desc[0] > desc[2])
435 return VAR_ERR_INCORRECT_CLASS_SPEC;
436 expand_range(desc[0], desc[2], chrclass);
439 chrclass[(int) *desc] = 1;
448 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
456 if (c >= '0' && c <= '7')
464 const char **src, char **dst, const char *end)
469 return VAR_ERR_INCOMPLETE_OCTAL;
470 if ( !expand_isoct(**src)
471 || !expand_isoct((*src)[1])
472 || !expand_isoct((*src)[2]))
473 return VAR_ERR_INVALID_OCTAL;
477 return VAR_ERR_OCTAL_TOO_LARGE;
496 if ((c >= '0' && c <= '9') ||
497 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
505 const char **src, char **dst, const char *end)
510 return VAR_ERR_INCOMPLETE_HEX;
511 if ( !expand_ishex(**src)
512 || !expand_ishex((*src)[1]))
513 return VAR_ERR_INVALID_HEX;
515 if (**src >= '0' && **src <= '9')
517 else if (**src >= 'a' && **src <= 'f')
518 c = **src - 'a' + 10;
519 else if (**src >= 'A' && **src <= 'F')
520 c = **src - 'A' + 10;
525 if (**src >= '0' && **src <= '9')
527 else if (**src >= 'a' && **src <= 'f')
528 c += **src - 'a' + 10;
529 else if (**src >= 'A' && **src <= 'F')
530 c += **src - 'A' + 10;
539 const char **src, char **dst, const char *end)
543 while (*src < end && **src != '}') {
544 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
549 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
556 const char **src, char **dst, const char *end)
559 return VAR_ERR_INCOMPLETE_HEX;
562 return expand_grouped_hex(src, dst, end);
564 return expand_simple_hex(src, dst, end);
569 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
573 /* forward declarations */
574 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
575 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
576 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
578 /* parse pattern text */
581 var_t *var, var_parse_t *ctx,
582 const char *begin, const char *end)
586 /* parse until '/' */
587 for (p = begin; p != end && *p != '/'; p++) {
588 if (*p == var->syntax.escape) {
590 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
597 /* parse substitution text */
600 var_t *var, var_parse_t *ctx,
601 const char *begin, const char *end)
605 /* parse until delim_init or '/' */
606 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
607 if (*p == var->syntax.escape) {
609 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
616 /* parse expression text */
619 var_t *var, var_parse_t *ctx,
620 const char *begin, const char *end)
624 /* parse until delim_init or delim_close or ':' */
625 for (p = begin; p != end
626 && *p != var->syntax.delim_init
627 && *p != var->syntax.delim_close
629 if (*p == var->syntax.escape) {
631 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
638 /* parse opertion argument text */
641 var_t *var, var_parse_t *ctx,
642 const char *begin, const char *end)
646 /* parse until delim_init or ')' */
647 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
648 if (*p == var->syntax.escape) {
650 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
658 parse_opargtext_or_variable(
659 var_t *var, var_parse_t *ctx,
660 const char *begin, const char *end,
667 tokenbuf_init(result);
673 rc = parse_opargtext(var, ctx, p, end);
677 if (!tokenbuf_append(result, p, rc)) {
678 rc = VAR_ERR_OUT_OF_MEMORY;
683 rc = parse_variable(var, ctx, p, end, &tmp);
688 if (!tokenbuf_merge(result, &tmp)) {
689 rc = VAR_ERR_OUT_OF_MEMORY;
693 tokenbuf_free(&tmp); /* KES 11/9/2003 */
700 tokenbuf_free(result);
704 /* parse expression or variable */
706 parse_exptext_or_variable(
707 var_t *var, var_parse_t *ctx,
708 const char *begin, const char *end,
711 const char *p = begin;
715 tokenbuf_init(result);
720 /* try to parse expression text */
721 rc = parse_exptext(var, ctx, p, end);
725 if (!tokenbuf_append(result, p, rc)) {
726 rc = VAR_ERR_OUT_OF_MEMORY;
732 /* try to parse variable construct */
733 rc = parse_variable(var, ctx, p, end, &tmp);
738 if (!tokenbuf_merge(result, &tmp)) {
739 rc = VAR_ERR_OUT_OF_MEMORY;
743 tokenbuf_free(&tmp); /* KES 11/9/2003 */
751 tokenbuf_free(result);
755 /* parse substitution text or variable */
757 parse_substext_or_variable(
758 var_t *var, var_parse_t *ctx,
759 const char *begin, const char *end,
762 const char *p = begin;
766 tokenbuf_init(result);
771 /* try to parse substitution text */
772 rc = parse_substext(var, ctx, p, end);
776 if (!tokenbuf_append(result, p, rc)) {
777 rc = VAR_ERR_OUT_OF_MEMORY;
783 /* try to parse substitution text */
784 rc = parse_variable(var, ctx, p, end, &tmp);
789 if (!tokenbuf_merge(result, &tmp)) {
790 rc = VAR_ERR_OUT_OF_MEMORY;
794 tokenbuf_free(&tmp); /* KES 11/9/2003 */
802 tokenbuf_free(result);
806 /* parse class description */
808 parse_class_description(
809 var_t *var, var_parse_t *ctx,
810 tokenbuf_t *src, tokenbuf_t *dst)
816 while (p != src->end) {
817 if ((src->end - p) >= 3 && p[1] == '-') {
819 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
820 for (c = *p, d = p[2]; c <= d; ++c) {
821 if (!tokenbuf_append(dst, (char *)&c, 1))
822 return VAR_ERR_OUT_OF_MEMORY;
826 if (!tokenbuf_append(dst, p, 1))
827 return VAR_ERR_OUT_OF_MEMORY;
834 /* parse regex replace part */
837 var_t *var, var_parse_t *ctx,
841 tokenbuf_t *expanded)
847 tokenbuf_init(expanded);
849 while (p != orig->end) {
851 if (orig->end - p <= 1) {
852 tokenbuf_free(expanded);
853 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
857 if (!tokenbuf_append(expanded, p, 1)) {
858 tokenbuf_free(expanded);
859 return VAR_ERR_OUT_OF_MEMORY;
864 if (!isdigit((int)*p)) {
865 tokenbuf_free(expanded);
866 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
870 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
871 tokenbuf_free(expanded);
872 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
874 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
875 pmatch[i].rm_eo - pmatch[i].rm_so)) {
876 tokenbuf_free(expanded);
877 return VAR_ERR_OUT_OF_MEMORY;
880 if (!tokenbuf_append(expanded, p, 1)) {
881 tokenbuf_free(expanded);
882 return VAR_ERR_OUT_OF_MEMORY;
891 /* operation: transpose */
894 var_t *var, var_parse_t *ctx,
899 tokenbuf_t srcclass, dstclass;
904 tokenbuf_init(&srcclass);
905 tokenbuf_init(&dstclass);
906 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
908 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
910 if (srcclass.begin == srcclass.end) {
911 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
914 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
915 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
918 if (data->buffer_size == 0) {
920 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
921 rc = VAR_ERR_OUT_OF_MEMORY;
924 tokenbuf_move(&tmp, data);
926 for (p = data->begin; p != data->end; ++p) {
927 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
928 if (*p == srcclass.begin[i]) {
929 *((char *)p) = dstclass.begin[i];
934 tokenbuf_free(&srcclass);
935 tokenbuf_free(&dstclass);
939 tokenbuf_free(search);
940 tokenbuf_free(replace);
941 tokenbuf_free(&srcclass);
942 tokenbuf_free(&dstclass);
946 /* operation: search & replace */
948 op_search_and_replace(
949 var_t *var, var_parse_t *ctx,
957 int case_insensitive = 0;
963 if (search->begin == search->end)
964 return VAR_ERR_EMPTY_SEARCH_STRING;
966 for (p = flags->begin; p != flags->end; p++) {
967 switch (tolower(*p)) {
972 case_insensitive = 1;
981 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
986 /* plain text pattern based operation */
988 for (p = data->begin; p != data->end;) {
989 if (case_insensitive)
990 rc = strncasecmp(p, search->begin, search->end - search->begin);
992 rc = strncmp(p, search->begin, search->end - search->begin);
994 /* not matched, copy character */
995 if (!tokenbuf_append(&tmp, p, 1)) {
997 return VAR_ERR_OUT_OF_MEMORY;
1001 /* matched, copy replacement string */
1002 tokenbuf_merge(&tmp, replace);
1003 p += (search->end - search->begin);
1005 /* append remaining text */
1006 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1007 tokenbuf_free(&tmp);
1008 return VAR_ERR_OUT_OF_MEMORY;
1014 tokenbuf_free(data);
1015 tokenbuf_move(&tmp, data);
1017 /* regular expression pattern based operation */
1019 tokenbuf_t myreplace;
1021 regmatch_t pmatch[10];
1024 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1025 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1026 return VAR_ERR_OUT_OF_MEMORY;
1027 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1028 tokenbuf_free(&tmp);
1029 return VAR_ERR_OUT_OF_MEMORY;
1032 /* compile the pattern. */
1033 rc = regcomp(&preg, tmp.begin,
1035 | (multiline ? REG_NEWLINE : 0)
1036 | (case_insensitive ? REG_ICASE : 0)));
1037 tokenbuf_free(&tmp);
1039 tokenbuf_free(&mydata);
1040 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1043 /* match the pattern and create the result string in the tmp buffer */
1044 tokenbuf_append(&tmp, "", 0);
1045 for (p = mydata.begin; p < mydata.end; ) {
1046 if (p == mydata.begin || p[-1] == '\n')
1049 regexec_flag = REG_NOTBOL;
1050 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1052 /* no (more) matching */
1053 tokenbuf_append(&tmp, p, mydata.end - p);
1057 && (p + pmatch[0].rm_so) == mydata.end
1058 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1059 /* special case: found empty pattern (usually /^/ or /$/ only)
1060 in multi-line at end of data (after the last newline) */
1061 tokenbuf_append(&tmp, p, mydata.end - p);
1065 /* append prolog string */
1066 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1068 tokenbuf_free(&tmp);
1069 tokenbuf_free(&mydata);
1070 return VAR_ERR_OUT_OF_MEMORY;
1072 /* create replace string */
1073 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1076 tokenbuf_free(&tmp);
1077 tokenbuf_free(&mydata);
1080 /* append replace string */
1081 if (!tokenbuf_merge(&tmp, &myreplace)) {
1083 tokenbuf_free(&tmp);
1084 tokenbuf_free(&mydata);
1085 tokenbuf_free(&myreplace);
1086 return VAR_ERR_OUT_OF_MEMORY;
1088 tokenbuf_free(&myreplace);
1089 /* skip now processed data */
1090 p += pmatch[0].rm_eo;
1091 /* if pattern matched an empty part (think about
1092 anchor-only regular expressions like /^/ or /$/) we
1093 skip the next character to make sure we do not enter
1094 an infinitive loop in matching */
1095 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1096 if (p >= mydata.end)
1098 if (!tokenbuf_append(&tmp, p, 1)) {
1100 tokenbuf_free(&tmp);
1101 tokenbuf_free(&mydata);
1102 return VAR_ERR_OUT_OF_MEMORY;
1106 /* append prolog string and stop processing if we
1107 do not perform the search & replace globally */
1109 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1111 tokenbuf_free(&tmp);
1112 tokenbuf_free(&mydata);
1113 return VAR_ERR_OUT_OF_MEMORY;
1120 tokenbuf_free(data);
1121 tokenbuf_move(&tmp, data);
1122 tokenbuf_free(&mydata);
1128 /* operation: offset substring */
1131 var_t *var, var_parse_t *ctx,
1140 /* determine begin of result string */
1141 if ((data->end - data->begin) < num1)
1142 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1143 p = data->begin + num1;
1145 /* if num2 is zero, we copy the rest from there. */
1147 if (!tokenbuf_assign(&res, p, data->end - p))
1148 return VAR_ERR_OUT_OF_MEMORY;
1150 /* ok, then use num2. */
1152 if ((p + num2) > data->end)
1153 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1154 if (!tokenbuf_assign(&res, p, num2))
1155 return VAR_ERR_OUT_OF_MEMORY;
1158 return VAR_ERR_OFFSET_LOGIC;
1159 if ((data->begin + num2) > data->end)
1160 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1161 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1162 return VAR_ERR_OUT_OF_MEMORY;
1165 tokenbuf_free(data);
1166 tokenbuf_move(&res, data);
1170 /* operation: padding */
1173 var_t *var, var_parse_t *ctx,
1182 if (fill->begin == fill->end)
1183 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1184 tokenbuf_init(&result);
1185 if (position == 'l') {
1187 i = width - (data->end - data->begin);
1189 i = i / (fill->end - fill->begin);
1191 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1192 return VAR_ERR_OUT_OF_MEMORY;
1195 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1196 if (!tokenbuf_append(data, fill->begin, i))
1197 return VAR_ERR_OUT_OF_MEMORY;
1199 } else if (position == 'r') {
1201 i = width - (data->end - data->begin);
1203 i = i / (fill->end - fill->begin);
1205 if (!tokenbuf_merge(&result, fill)) {
1206 tokenbuf_free(&result);
1207 return VAR_ERR_OUT_OF_MEMORY;
1211 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1212 if (!tokenbuf_append(&result, fill->begin, i)) {
1213 tokenbuf_free(&result);
1214 return VAR_ERR_OUT_OF_MEMORY;
1216 if (!tokenbuf_merge(&result, data)) {
1217 tokenbuf_free(&result);
1218 return VAR_ERR_OUT_OF_MEMORY;
1220 /* move string from temporary buffer to data buffer */
1221 tokenbuf_free(data);
1222 tokenbuf_move(&result, data);
1224 } else if (position == 'c') {
1225 /* centered padding */
1226 i = (width - (data->end - data->begin)) / 2;
1228 /* create the prefix */
1229 i = i / (fill->end - fill->begin);
1231 if (!tokenbuf_merge(&result, fill)) {
1232 tokenbuf_free(&result);
1233 return VAR_ERR_OUT_OF_MEMORY;
1237 i = ((width - (data->end - data->begin)) / 2)
1238 % (fill->end - fill->begin);
1239 if (!tokenbuf_append(&result, fill->begin, i)) {
1240 tokenbuf_free(&result);
1241 return VAR_ERR_OUT_OF_MEMORY;
1243 /* append the actual data string */
1244 if (!tokenbuf_merge(&result, data)) {
1245 tokenbuf_free(&result);
1246 return VAR_ERR_OUT_OF_MEMORY;
1248 /* append the suffix */
1249 i = width - (result.end - result.begin);
1250 i = i / (fill->end - fill->begin);
1252 if (!tokenbuf_merge(&result, fill)) {
1253 tokenbuf_free(&result);
1254 return VAR_ERR_OUT_OF_MEMORY;
1258 i = width - (result.end - result.begin);
1259 if (!tokenbuf_append(&result, fill->begin, i)) {
1260 tokenbuf_free(&result);
1261 return VAR_ERR_OUT_OF_MEMORY;
1263 /* move string from temporary buffer to data buffer */
1264 tokenbuf_free(data);
1265 tokenbuf_move(&result, data);
1271 /* parse an integer number ("123") */
1274 var_t *var, var_parse_t *ctx,
1275 const char *begin, const char *end,
1283 while (isdigit(*p) && p != end) {
1293 /* parse an operation (":x...") */
1296 var_t *var, var_parse_t *ctx,
1297 const char *begin, const char *end,
1301 tokenbuf_t tmptokbuf;
1302 tokenbuf_t search, replace, flags;
1303 tokenbuf_t number1, number2;
1309 /* initialization */
1310 tokenbuf_init(&tmptokbuf);
1311 tokenbuf_init(&search);
1312 tokenbuf_init(&replace);
1313 tokenbuf_init(&flags);
1314 tokenbuf_init(&number1);
1315 tokenbuf_init(&number2);
1320 /* dispatch through the first operation character */
1321 switch (tolower(*p)) {
1323 /* turn value to lowercase. */
1324 if (data->begin != NULL) {
1325 /* if the buffer does not live in an allocated buffer,
1326 we have to copy it before modifying the contents. */
1327 if (data->buffer_size == 0) {
1328 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1329 rc = VAR_ERR_OUT_OF_MEMORY;
1334 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1335 *ptr = (char)tolower((int)(*ptr));
1341 /* turn value to uppercase. */
1342 if (data->begin != NULL) {
1343 /* if the buffer does not live in an allocated buffer,
1344 we have to copy it before modifying the contents. */
1345 if (data->buffer_size == 0) {
1346 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1347 rc = VAR_ERR_OUT_OF_MEMORY;
1352 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1353 *ptr = (char)toupper((int)(*ptr));
1359 /* cut out substring of value. */
1361 rc = parse_integer(var, ctx, p, end, &num1);
1363 rc = VAR_ERR_MISSING_START_OFFSET;
1372 } else if (*p == '-') {
1376 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1379 rc = parse_integer(var, ctx, p, end, &num2);
1381 if (data->begin != NULL) {
1382 rc = op_offset(var, ctx, data, num1, num2, isrange);
1389 /* determine length of the value */
1390 if (data->begin != NULL) {
1391 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1392 sprintf(buf, "%d", (int)(data->end - data->begin));
1393 tokenbuf_free(data);
1394 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1395 rc = VAR_ERR_OUT_OF_MEMORY;
1403 /* substitute parameter if data is empty */
1405 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1409 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1413 if (tokenbuf_isundef(data))
1414 tokenbuf_move(&tmptokbuf, data);
1415 else if (tokenbuf_isempty(data)) {
1416 tokenbuf_free(data);
1417 tokenbuf_move(&tmptokbuf, data);
1422 /* substitute empty string if data is not empty, parameter otherwise. */
1424 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1428 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1432 if (data->begin != NULL) {
1433 if (data->begin == data->end) {
1434 tokenbuf_free(data);
1435 tokenbuf_move(&tmptokbuf, data);
1437 tokenbuf_free(data);
1438 data->begin = data->end = "";
1439 data->buffer_size = 0;
1445 /* substitute parameter if data is not empty. */
1447 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1451 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1455 if (data->begin != NULL && data->begin != data->end) {
1456 tokenbuf_free(data);
1457 tokenbuf_move(&tmptokbuf, data);
1462 /* search and replace. */
1465 return VAR_ERR_MALFORMATTED_REPLACE;
1467 rc = parse_pattern(var, ctx, p, end);
1470 tokenbuf_set(&search, p, p + rc, 0);
1473 rc = VAR_ERR_MALFORMATTED_REPLACE;
1477 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1482 rc = VAR_ERR_MALFORMATTED_REPLACE;
1486 rc = parse_exptext(var, ctx, p, end);
1489 tokenbuf_set(&flags, p, p + rc, 0);
1491 if (data->begin != NULL) {
1492 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1499 /* transpose characters from class A to class B. */
1502 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1504 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1509 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1513 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1518 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1523 rc = op_transpose(var, ctx, data, &search, &replace);
1533 return VAR_ERR_MALFORMATTED_PADDING;
1535 rc = parse_integer(var, ctx, p, end, &num1);
1537 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1542 rc = VAR_ERR_MALFORMATTED_PADDING;
1546 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1551 rc = VAR_ERR_MALFORMATTED_PADDING;
1555 if (*p != 'l' && *p != 'c' && *p != 'r') {
1556 rc = VAR_ERR_MALFORMATTED_PADDING;
1561 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1568 /* operation callback function */
1571 const char *arg_ptr;
1573 const char *val_ptr;
1575 const char *out_ptr;
1581 rc = parse_name(var, ctx, p, end);
1589 tokenbuf_init(&args);
1590 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1594 arg_ptr = args.begin;
1595 arg_len = args.end - args.begin;
1597 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1606 val_ptr = data->begin;
1607 val_len = data->end - data->begin;
1609 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1610 /* call operation callback function */
1611 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1615 &out_ptr, &out_len, &out_size);
1617 if (arg_ptr != NULL)
1618 free((void *)arg_ptr);
1621 tokenbuf_free(data);
1622 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1624 if (arg_ptr != NULL)
1625 free((void *)arg_ptr);
1629 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1632 /* return successfully */
1633 tokenbuf_free(&tmptokbuf);
1634 tokenbuf_free(&search);
1635 tokenbuf_free(&replace);
1636 tokenbuf_free(&flags);
1637 tokenbuf_free(&number1);
1638 tokenbuf_free(&number2);
1641 /* return with an error */
1643 tokenbuf_free(data);
1644 tokenbuf_free(&tmptokbuf);
1645 tokenbuf_free(&search);
1646 tokenbuf_free(&replace);
1647 tokenbuf_free(&flags);
1648 tokenbuf_free(&number1);
1649 tokenbuf_free(&number2);
1653 /* parse numerical expression operand */
1655 parse_numexp_operand(
1656 var_t *var, var_parse_t *ctx,
1657 const char *begin, const char *end,
1658 int *result, int *failed)
1665 /* initialization */
1667 tokenbuf_init(&tmp);
1669 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1671 /* parse opening numerical expression */
1673 /* parse inner numerical expression */
1674 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1679 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1680 /* parse closing parenthesis */
1682 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1685 /* parse contained variable */
1686 else if (*p == var->syntax.delim_init) {
1687 /* parse variable with forced expansion */
1688 ctx = var_parse_push(ctx, &myctx);
1689 ctx->force_expand = 1;
1690 rc = parse_variable(var, ctx, p, end, &tmp);
1691 ctx = var_parse_pop(ctx);
1693 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1695 /* parse variable without forced expansion */
1696 ctx = var_parse_push(ctx, &myctx);
1697 ctx->force_expand = 0;
1698 rc = parse_variable(var, ctx, p, end, &tmp);
1699 ctx = var_parse_pop(ctx);
1704 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1705 } else if (rc < 0) {
1709 /* parse remaining numerical expression */
1710 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1711 tokenbuf_free(&tmp);
1716 /* parse relative index mark ("#") */
1717 else if ( var->syntax.index_mark != EOS
1718 && *p == var->syntax.index_mark) {
1720 *result = ctx->index_this;
1721 if (ctx->rel_lookup_flag)
1722 ctx->rel_lookup_cnt++;
1724 /* parse plain integer number */
1725 else if (isdigit(*p)) {
1726 rc = parse_integer(var, ctx, p, end, result);
1729 /* parse signed positive integer number */
1730 else if (*p == '+') {
1731 if ((end - p) > 1 && isdigit(p[1])) {
1733 rc = parse_integer(var, ctx, p, end, result);
1737 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1739 /* parse signed negative integer number */
1740 else if (*p == '-') {
1741 if (end - p > 1 && isdigit(p[1])) {
1743 rc = parse_integer(var, ctx, p, end, result);
1744 *result = -(*result);
1748 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1750 /* else we failed to parse anything reasonable */
1752 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1757 /* parse numerical expression ("x+y") */
1760 var_t *var, var_parse_t *ctx,
1761 const char *begin, const char *end,
1762 int *result, int *failed)
1769 /* initialization */
1772 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1774 /* parse left numerical operand */
1775 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1780 /* parse numerical operator */
1782 if (*p == '+' || *p == '-') {
1784 /* recursively parse right operand (light binding) */
1785 rc = parse_numexp(var, ctx, p, end, &right, failed);
1790 *result = (*result + right);
1792 *result = (*result - right);
1794 else if (*p == '*' || *p == '/' || *p == '%') {
1796 /* recursively parse right operand (string binding) */
1797 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1802 *result = (*result * right);
1803 else if (op == '/') {
1808 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1811 *result = (*result / right);
1813 else if (op == '%') {
1818 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1821 *result = (*result % right);
1828 /* return amount of parsed input */
1832 /* parse variable name ("abc") */
1835 var_t *var, var_parse_t *ctx,
1836 const char *begin, const char *end)
1840 /* parse as long as name class characters are found */
1841 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1846 /* lookup a variable value through the callback function */
1849 var_t *var, var_parse_t *ctx,
1850 const char *var_ptr, int var_len, int var_inc, int var_idx,
1851 const char **val_ptr, int *val_len, int *val_size)
1856 /* pass through to original callback */
1857 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1858 var_ptr, var_len, var_inc, var_idx,
1859 val_ptr, val_len, val_size);
1861 /* convert undefined variable into empty variable if relative
1862 lookups are counted. This is the case inside an active loop
1863 construct if no limits are given. There the parse_input()
1864 has to proceed until all variables have undefined values.
1865 This trick here allows it to determine this case. */
1866 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1867 ctx->rel_lookup_cnt--;
1869 /* ****FIXME**** passing back stack variable!!! */
1879 /* parse complex variable construct ("${name...}") */
1881 parse_variable_complex(
1882 var_t *var, var_parse_t *ctx,
1883 const char *begin, const char *end,
1888 int len, buffer_size;
1896 /* initializations */
1898 tokenbuf_init(&name);
1899 tokenbuf_init(&tmp);
1900 tokenbuf_init(result);
1902 /* parse open delimiter */
1903 if (p == end || *p != var->syntax.delim_open)
1907 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1909 /* parse name of variable to expand. The name may consist of an
1910 arbitrary number of variable name character and contained variable
1913 /* parse a variable name */
1914 rc = parse_name(var, ctx, p, end);
1918 if (!tokenbuf_append(&name, p, rc)) {
1919 rc = VAR_ERR_OUT_OF_MEMORY;
1925 /* parse an (embedded) variable */
1926 rc = parse_variable(var, ctx, p, end, &tmp);
1930 if (!tokenbuf_merge(&name, &tmp)) {
1931 rc = VAR_ERR_OUT_OF_MEMORY;
1936 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1939 /* we must have the complete expanded variable name now,
1940 so make sure we really do. */
1941 if (name.begin == name.end) {
1942 if (ctx->force_expand) {
1943 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1947 /* If no force_expand is requested, we have to back-off.
1948 We're not sure whether our approach here is 100% correct,
1949 because it _could_ have side-effects according to Peter
1950 Simons, but as far as we know and tried it, it is
1951 correct. But be warned -- RSE */
1952 tokenbuf_set(result, begin - 1, p, 0);
1957 /* parse an optional index specification */
1958 if ( var->syntax.index_open != EOS
1959 && *p == var->syntax.index_open) {
1961 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1965 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1970 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1973 if (*p != var->syntax.index_close) {
1974 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1980 /* parse end of variable construct or start of post-operations */
1981 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1982 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1985 if ((inc = (*p++ == '+'))) {
1986 p++; /* skip the + */
1989 /* lookup the variable value now */
1991 tokenbuf_set(result, begin - 1, p, 0);
1993 rc = lookup_value(var, ctx,
1994 name.begin, name.end-name.begin, inc, idx,
1995 &data, &len, &buffer_size);
1996 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1997 tokenbuf_init(result); /* delayed handling of undefined variable */
1998 } else if (rc < 0) {
2001 /* the preliminary result is the raw value of the variable.
2002 This may be modified by the operations that may follow. */
2003 tokenbuf_set(result, data, data + len, buffer_size);
2007 /* parse optional post-operations */
2010 tokenbuf_free(&tmp);
2011 tokenbuf_init(&tmp);
2013 while (p != end && *p == ':') {
2016 rc = parse_operation(var, ctx, p, end, result);
2018 rc = parse_operation(var, ctx, p, end, &tmp);
2025 if (p == end || *p != var->syntax.delim_close) {
2026 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2032 } else if (p[-1] == '+') {
2036 /* lazy handling of undefined variable */
2037 if (!failed && tokenbuf_isundef(result)) {
2038 if (ctx->force_expand) {
2039 rc = VAR_ERR_UNDEFINED_VARIABLE;
2042 tokenbuf_set(result, begin - 1, p, 0);
2046 /* return successfully */
2047 tokenbuf_free(&name);
2048 tokenbuf_free(&tmp);
2051 /* return with an error */
2053 tokenbuf_free(&name);
2054 tokenbuf_free(&tmp);
2055 tokenbuf_free(result);
2059 /* parse variable construct ("$name" or "${name...}") */
2062 var_t *var, var_parse_t *ctx,
2063 const char *begin, const char *end,
2068 int len, buffer_size;
2072 /* initialization */
2074 tokenbuf_init(result);
2076 /* parse init delimiter */
2077 if (p == end || *p != var->syntax.delim_init)
2081 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2083 /* parse a simple variable name.
2084 (if this fails, we're try to parse a complex variable construct) */
2085 rc = parse_name(var, ctx, p, end);
2089 inc = (p[rc] == '+');
2090 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2091 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2092 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2097 tokenbuf_set(result, data, data + len, buffer_size);
2101 /* parse a complex variable construct (else case) */
2102 rc = parse_variable_complex(var, ctx, p, end, result);
2108 /* parse loop construct limits ("[...]{b,s,e}") */
2111 var_t *var, var_parse_t *ctx,
2112 const char *begin, const char *end,
2113 int *start, int *step, int *stop, int *open_stop)
2119 /* initialization */
2122 /* we are happy if nothing is to left to parse */
2126 /* parse start delimiter */
2127 if (*p != var->syntax.delim_open)
2131 /* parse loop start value */
2133 rc = parse_numexp(var, ctx, p, end, start, &failed);
2134 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2135 *start = 0; /* use default */
2137 return (var_rc_t)rc;
2141 return VAR_ERR_UNDEFINED_VARIABLE;
2143 /* parse separator */
2145 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2148 /* parse loop step value */
2150 rc = parse_numexp(var, ctx, p, end, step, &failed);
2151 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2152 *step = 1; /* use default */
2154 return (var_rc_t)rc;
2158 return VAR_ERR_UNDEFINED_VARIABLE;
2160 /* parse separator */
2162 /* if not found, parse end delimiter */
2163 if (*p != var->syntax.delim_close)
2164 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2167 /* shift step value to stop value */
2171 /* determine whether loop end is open */
2176 return (var_rc_t)(p - begin);
2180 /* parse loop stop value */
2182 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2183 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2184 *stop = 0; /* use default */
2188 return (var_rc_t)rc;
2194 return VAR_ERR_UNDEFINED_VARIABLE;
2196 /* parse end delimiter */
2197 if (*p != var->syntax.delim_close)
2198 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2201 /* return amount of parsed input */
2202 return (var_rc_t)(p - begin);
2205 /* parse plain text */
2208 var_t *var, var_parse_t *ctx,
2209 const char *begin, const char *end)
2213 /* parse until delim_init (variable construct)
2214 or index_open (loop construct) is found */
2215 for (p = begin; p != end; p++) {
2216 if (*p == var->syntax.escape) {
2217 p++; /* skip next character */
2219 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2221 else if (*p == var->syntax.delim_init)
2223 else if ( var->syntax.index_open != EOS
2224 && ( *p == var->syntax.index_open
2225 || *p == var->syntax.index_close))
2231 /* expand input in general */
2234 var_t *var, var_parse_t *ctx,
2235 const char *begin, const char *end,
2236 tokenbuf_t *output, int recursion_level)
2241 int start, step, stop, open_stop;
2245 int loop_limit_length;
2248 /* initialization */
2252 /* try to parse a loop construct */
2254 && var->syntax.index_open != EOS
2255 && *p == var->syntax.index_open) {
2258 /* loop preparation */
2259 loop_limit_length = -1;
2260 rel_lookup_cnt = ctx->rel_lookup_cnt;
2268 /* iterate over loop construct, either as long as there is
2269 (still) nothing known about the limit, or there is an open
2270 (=unknown) limit stop and there are still defined variables
2271 or there is a stop limit known and it is still not reached */
2275 && ( loop_limit_length < 0
2276 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2281 /* remember current output end for restoring */
2282 output_backup = (output->end - output->begin);
2284 /* open temporary context for recursion */
2285 ctx = var_parse_push(ctx, &myctx);
2286 ctx->force_expand = 1;
2287 ctx->rel_lookup_flag = 1;
2288 ctx->index_this = i;
2290 /* recursive parse input through ourself */
2291 rc = parse_input(var, ctx, p, end,
2292 output, recursion_level+1);
2294 /* retrieve info and close temporary context */
2295 rel_lookup_cnt = ctx->rel_lookup_cnt;
2296 ctx = var_parse_pop(ctx);
2298 /* error handling */
2302 /* make sure the loop construct is closed */
2303 if (p[rc] != var->syntax.index_close) {
2304 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2308 /* try to parse loop construct limit specification */
2309 if (loop_limit_length < 0) {
2310 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2311 &start, &step, &stop, &open_stop);
2315 loop_limit_length = 0;
2317 loop_limit_length = rc2;
2318 /* restart loop from scratch */
2319 output->end = (output->begin + output_backup);
2325 /* if stop value is open, restore to the output end
2326 because the last iteration was just to determine the loop
2327 termination and its result has to be discarded */
2329 output->end = (output->begin + output_backup);
2331 /* skip parsed loop construct */
2334 p += loop_limit_length;
2339 /* try to parse plain text */
2340 rc = parse_text(var, ctx, p, end);
2342 if (!tokenbuf_append(output, p, rc)) {
2343 rc = VAR_ERR_OUT_OF_MEMORY;
2351 /* try to parse a variable construct */
2352 tokenbuf_init(&result);
2353 rc = parse_variable(var, ctx, p, end, &result);
2355 if (!tokenbuf_merge(output, &result)) {
2356 tokenbuf_free(&result);
2357 rc = VAR_ERR_OUT_OF_MEMORY;
2360 tokenbuf_free(&result);
2364 tokenbuf_free(&result);
2368 } while (p != end && rc > 0);
2370 /* We do not know whether this really could happen, but because we
2371 are paranoid, report an error at the outer most parsing level if
2372 there is still any input. Because this would mean that we are no
2373 longer able to parse the remaining input as a loop construct, a
2374 text or a variable construct. This would be very strange, but
2375 could perhaps happen in case of configuration errors!?... */
2376 if (recursion_level == 0 && p != end) {
2377 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2381 /* return amount of parsed text */
2382 return (var_rc_t)(p - begin);
2384 /* return with an error where as a special case the output begin is
2385 set to the input begin and the output end to the last input parsing
2388 tokenbuf_free(output);
2389 tokenbuf_set(output, begin, p, 0);
2390 return (var_rc_t)rc;
2395 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2399 /* create variable expansion context */
2407 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2408 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2409 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2410 memset(var, 0, sizeof(var_t));
2411 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2416 /* destroy variable expansion context */
2422 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2427 /* configure variable expansion context */
2435 var_rc_t rc = VAR_OK;
2438 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2441 case VAR_CONFIG_SYNTAX: {
2443 s = (var_syntax_t *)va_arg(ap, void *);
2445 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2446 var->syntax.escape = s->escape;
2447 var->syntax.delim_init = s->delim_init;
2448 var->syntax.delim_open = s->delim_open;
2449 var->syntax.delim_close = s->delim_close;
2450 var->syntax.index_open = s->index_open;
2451 var->syntax.index_close = s->index_close;
2452 var->syntax.index_mark = s->index_mark;
2453 var->syntax.name_chars = NULL; /* unused internally */
2454 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2456 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2457 || var->syntax_nameclass[(int)var->syntax.delim_open]
2458 || var->syntax_nameclass[(int)var->syntax.delim_close]
2459 || var->syntax_nameclass[(int)var->syntax.escape])
2460 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2463 case VAR_CONFIG_CB_VALUE: {
2466 fct = (var_cb_value_t)va_arg(ap, void *);
2467 ctx = (void *)va_arg(ap, void *);
2468 var->cb_value_fct = fct;
2469 var->cb_value_ctx = ctx;
2472 case VAR_CONFIG_CB_OPERATION: {
2473 var_cb_operation_t fct;
2475 fct = (var_cb_operation_t)va_arg(ap, void *);
2476 ctx = (void *)va_arg(ap, void *);
2477 var->cb_operation_fct = fct;
2478 var->cb_operation_ctx = ctx;
2482 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2488 /* perform unescape operation on a buffer */
2492 const char *src, int srclen,
2493 char *dst, int dstlen,
2499 if (var == NULL || src == NULL || dst == NULL)
2500 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2505 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2524 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2527 case '0': case '1': case '2': case '3': case '4':
2528 case '5': case '6': case '7': case '8': case '9':
2530 && isdigit((int)src[1])
2531 && isdigit((int)src[2])) {
2532 if ((rc = expand_octal(&src, &dst, end)) != 0)
2550 /* perform expand operation on a buffer */
2554 const char *src_ptr, int src_len,
2555 char **dst_ptr, int *dst_len,
2562 /* argument sanity checks */
2563 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2564 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2566 /* prepare internal expansion context */
2568 ctx.force_expand = force_expand;
2569 ctx.rel_lookup_flag = 0;
2570 ctx.rel_lookup_cnt = 0;
2573 /* start the parsing */
2574 tokenbuf_init(&output);
2575 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2577 /* post-processing */
2579 /* always EOS-terminate output for convinience reasons
2580 but do not count the EOS-terminator in the length */
2581 if (!tokenbuf_append(&output, "\0", 1)) {
2582 tokenbuf_free(&output);
2583 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2587 /* provide result */
2588 *dst_ptr = (char *)output.begin;
2589 if (dst_len != NULL)
2590 *dst_len = (output.end - output.begin);
2594 /* provide result */
2595 if (dst_len != NULL)
2596 *dst_len = (output.end - output.begin);
2602 /* format and expand a string */
2606 char **dst_ptr, int force_expand,
2607 const char *fmt, va_list ap)
2613 /* argument sanity checks */
2614 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2615 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2617 /* perform formatting */
2618 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2619 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2620 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2623 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2626 /* perform expansion */
2627 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2638 /* format and expand a string */
2642 char **dst_ptr, int force_expand,
2643 const char *fmt, ...)
2648 /* argument sanity checks */
2649 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2650 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2653 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2659 /* var_rc_t to string mapping table */
2660 static const char *var_errors[] = {
2661 _("everything ok"), /* VAR_OK = 0 */
2662 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2663 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2664 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2665 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2666 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2667 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2668 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2669 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC */
2670 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2671 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2672 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2673 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2674 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2675 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2676 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE */
2677 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2678 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2679 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2680 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING */
2681 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET */
2682 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2683 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2684 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2685 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2686 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2687 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2688 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2689 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2690 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2691 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH */
2692 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2693 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2694 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2695 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2696 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2697 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2698 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2699 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2700 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2701 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2702 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2703 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2704 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2705 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2706 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2709 /* translate a return code into its corresponding descriptive text */
2710 const char *var_strerror(var_t *var, var_rc_t rc)
2713 rc = (var_rc_t)(0 - rc);
2714 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2715 str = _("unknown error");
2717 str = (char *)var_errors[rc];