2 ** OSSP var - Variable Expansion
3 ** Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
4 ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
5 ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
7 ** This file is part of OSSP var, a variable expansion
8 ** library which can be found at http://www.ossp.org/pkg/lib/var/.
10 ** Permission to use, copy, modify, and distribute this software for
11 ** any purpose with or without fee is hereby granted, provided that
12 ** the above copyright notice and this permission notice appear in all
15 ** For disclaimer see below.
18 * Adapted by Kern Sibbald to Bacula June 2003
21 Copyright (C) 2000-2004 Kern Sibbald and John Walker
23 This program is free software; you can redistribute it and/or
24 modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation; either version 2 of
26 the License, or (at your option) any later version.
28 This program is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31 General Public License for more details.
33 You should have received a copy of the GNU General Public
34 License along with this program; if not, write to the Free
35 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40 #if !defined(HAVE_WIN32)
42 #if defined(HAVE_PCREPOSIX)
43 # include <pcreposix.h>
44 #elif defined(HAVE_WIN32)
51 /* support for OSSP ex based exception throwing */
55 ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \
56 ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
58 #define VAR_RC(rv) (rv)
67 ** ==== INTERNAL DATA STRUCTURES ====
71 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
73 /* the external context structure */
76 char_class_t syntax_nameclass;
77 var_cb_value_t cb_value_fct;
79 var_cb_operation_t cb_operation_fct;
80 void *cb_operation_ctx;
83 /* the internal expansion context structure */
85 struct var_parse_st *lower;
91 typedef struct var_parse_st var_parse_t;
93 /* the default syntax configuration */
94 static const var_syntax_t var_syntax_default = {
98 '}', /* delim_close */
100 ']', /* index_close */
101 '#', /* index_mark */
102 "a-zA-Z0-9_" /* name_chars */
107 ** ==== FORMATTING FUNCTIONS ====
111 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
114 int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx,
115 const char *format, va_list ap)
117 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
118 char ibuf[((sizeof(int)*8)/3)+10];
128 while (*format != '\0') {
129 if (*format == '%') {
138 c = (char)va_arg(ap, int);
144 if ((cp = (char *)va_arg(ap, char *)) == NULL)
150 d = (int)va_arg(ap, int);
151 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
165 if ((format = strchr(cp, '%')) == NULL)
166 format = strchr(cp, '\0');
169 /* perform output operation */
171 if ((n = output(ctx, cp, n)) == -1)
178 /* output callback function context for var_mvsnprintf() */
182 } var_mvsnprintf_cb_t;
184 /* output callback function for var_mvsnprintf() */
188 const char *buffer, int bufsize)
190 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
192 if (bufsize > ctx->buflen)
194 memcpy(ctx->bufptr, buffer, bufsize);
195 ctx->bufptr += bufsize;
196 ctx->buflen -= bufsize;
200 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
203 char *buffer, int bufsize,
204 const char *format, va_list ap)
207 var_mvsnprintf_cb_t ctx;
211 if (buffer != NULL && bufsize == 0)
214 /* just determine output length */
215 n = var_mvxprintf(NULL, NULL, format, ap);
217 /* perform real output */
219 ctx.buflen = bufsize;
220 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
221 if (n != -1 && ctx.buflen == 0)
224 *(ctx.bufptr) = '\0';
231 ** ==== PARSE CONTEXT FUNCTIONS ====
237 var_parse_t *lower, var_parse_t *upper)
241 memcpy(upper, lower, sizeof(var_parse_t));
242 upper->lower = lower;
257 ** ==== TOKEN BUFFER FUNCTIONS ====
261 #define TOKENBUF_INITIAL_BUFSIZE 64
275 buf->buffer_size = 0;
283 if (buf->begin == NULL && buf->end == NULL)
292 if (buf->begin == buf->end)
299 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
303 buf->buffer_size = buffer_size;
309 tokenbuf_t *src, tokenbuf_t *dst)
311 dst->begin = src->begin;
313 dst->buffer_size = src->buffer_size;
320 tokenbuf_t *buf, const char *data, int len)
324 if ((p = (char *)malloc(len + 1)) == NULL)
326 memcpy(p, data, len);
329 buf->buffer_size = len + 1;
330 *((char *)(buf->end)) = EOS;
336 tokenbuf_t *output, const char *data, int len)
342 /* Is the tokenbuffer initialized at all? If not, allocate a
343 standard-sized buffer to begin with. */
344 if (output->begin == NULL) {
345 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
347 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
350 /* does the token contain text, but no buffer has been allocated yet? */
351 if (output->buffer_size == 0) {
352 /* check whether data borders to output. If, we can append
353 simly by increasing the end pointer. */
354 if (output->end == data) {
358 /* ok, so copy the contents of output into an allocated buffer
359 so that we can append that way. */
360 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
362 memcpy(tmp, output->begin, output->end - output->begin);
363 output->buffer_size = output->end - output->begin;
365 output->end = tmp + output->buffer_size;
366 output->buffer_size += len + 1;
369 /* does the token fit into the current buffer? If not, realloc a
370 larger buffer that fits. */
371 if ((output->buffer_size - (output->end - output->begin)) <= len) {
372 new_size = output->buffer_size;
375 } while ((new_size - (output->end - output->begin)) <= len);
376 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
378 output->end = new_buffer + (output->end - output->begin);
379 output->begin = new_buffer;
380 output->buffer_size = new_size;
383 /* append the data at the end of the current buffer. */
385 memcpy((char *)output->end, data, len);
387 *((char *)output->end) = EOS;
393 tokenbuf_t *output, tokenbuf_t *input)
395 return tokenbuf_append(output, input->begin, input->end - input->begin);
402 if (buf->begin != NULL && buf->buffer_size > 0)
403 free((char *)buf->begin);
404 buf->begin = buf->end = NULL;
405 buf->buffer_size = 0;
411 ** ==== CHARACTER CLASS EXPANSION ====
416 expand_range(char a, char b, char_class_t chrclass)
419 chrclass[(int)a] = 1;
425 expand_character_class(const char *desc, char_class_t chrclass)
429 /* clear the class array. */
430 for (i = 0; i < 256; ++i)
433 /* walk through class description and set appropriate entries in array */
434 while (*desc != EOS) {
435 if (desc[1] == '-' && desc[2] != EOS) {
436 if (desc[0] > desc[2])
437 return VAR_ERR_INCORRECT_CLASS_SPEC;
438 expand_range(desc[0], desc[2], chrclass);
441 chrclass[(int) *desc] = 1;
450 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
458 if (c >= '0' && c <= '7')
466 const char **src, char **dst, const char *end)
471 return VAR_ERR_INCOMPLETE_OCTAL;
472 if ( !expand_isoct(**src)
473 || !expand_isoct((*src)[1])
474 || !expand_isoct((*src)[2]))
475 return VAR_ERR_INVALID_OCTAL;
479 return VAR_ERR_OCTAL_TOO_LARGE;
498 if ((c >= '0' && c <= '9') ||
499 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
507 const char **src, char **dst, const char *end)
512 return VAR_ERR_INCOMPLETE_HEX;
513 if ( !expand_ishex(**src)
514 || !expand_ishex((*src)[1]))
515 return VAR_ERR_INVALID_HEX;
517 if (**src >= '0' && **src <= '9')
519 else if (**src >= 'a' && **src <= 'f')
520 c = **src - 'a' + 10;
521 else if (**src >= 'A' && **src <= 'F')
522 c = **src - 'A' + 10;
527 if (**src >= '0' && **src <= '9')
529 else if (**src >= 'a' && **src <= 'f')
530 c += **src - 'a' + 10;
531 else if (**src >= 'A' && **src <= 'F')
532 c += **src - 'A' + 10;
541 const char **src, char **dst, const char *end)
545 while (*src < end && **src != '}') {
546 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
551 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
558 const char **src, char **dst, const char *end)
561 return VAR_ERR_INCOMPLETE_HEX;
564 return expand_grouped_hex(src, dst, end);
566 return expand_simple_hex(src, dst, end);
571 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
575 /* forward declarations */
576 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
577 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
578 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
580 /* parse pattern text */
583 var_t *var, var_parse_t *ctx,
584 const char *begin, const char *end)
588 /* parse until '/' */
589 for (p = begin; p != end && *p != '/'; p++) {
590 if (*p == var->syntax.escape) {
592 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
599 /* parse substitution text */
602 var_t *var, var_parse_t *ctx,
603 const char *begin, const char *end)
607 /* parse until delim_init or '/' */
608 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
609 if (*p == var->syntax.escape) {
611 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
618 /* parse expression text */
621 var_t *var, var_parse_t *ctx,
622 const char *begin, const char *end)
626 /* parse until delim_init or delim_close or ':' */
627 for (p = begin; p != end
628 && *p != var->syntax.delim_init
629 && *p != var->syntax.delim_close
631 if (*p == var->syntax.escape) {
633 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
640 /* parse opertion argument text */
643 var_t *var, var_parse_t *ctx,
644 const char *begin, const char *end)
648 /* parse until delim_init or ')' */
649 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
650 if (*p == var->syntax.escape) {
652 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
660 parse_opargtext_or_variable(
661 var_t *var, var_parse_t *ctx,
662 const char *begin, const char *end,
669 tokenbuf_init(result);
675 rc = parse_opargtext(var, ctx, p, end);
679 if (!tokenbuf_append(result, p, rc)) {
680 rc = VAR_ERR_OUT_OF_MEMORY;
685 rc = parse_variable(var, ctx, p, end, &tmp);
690 if (!tokenbuf_merge(result, &tmp)) {
691 rc = VAR_ERR_OUT_OF_MEMORY;
695 tokenbuf_free(&tmp); /* KES 11/9/2003 */
702 tokenbuf_free(result);
706 /* parse expression or variable */
708 parse_exptext_or_variable(
709 var_t *var, var_parse_t *ctx,
710 const char *begin, const char *end,
713 const char *p = begin;
717 tokenbuf_init(result);
722 /* try to parse expression text */
723 rc = parse_exptext(var, ctx, p, end);
727 if (!tokenbuf_append(result, p, rc)) {
728 rc = VAR_ERR_OUT_OF_MEMORY;
734 /* try to parse variable construct */
735 rc = parse_variable(var, ctx, p, end, &tmp);
740 if (!tokenbuf_merge(result, &tmp)) {
741 rc = VAR_ERR_OUT_OF_MEMORY;
745 tokenbuf_free(&tmp); /* KES 11/9/2003 */
753 tokenbuf_free(result);
757 /* parse substitution text or variable */
759 parse_substext_or_variable(
760 var_t *var, var_parse_t *ctx,
761 const char *begin, const char *end,
764 const char *p = begin;
768 tokenbuf_init(result);
773 /* try to parse substitution text */
774 rc = parse_substext(var, ctx, p, end);
778 if (!tokenbuf_append(result, p, rc)) {
779 rc = VAR_ERR_OUT_OF_MEMORY;
785 /* try to parse substitution text */
786 rc = parse_variable(var, ctx, p, end, &tmp);
791 if (!tokenbuf_merge(result, &tmp)) {
792 rc = VAR_ERR_OUT_OF_MEMORY;
796 tokenbuf_free(&tmp); /* KES 11/9/2003 */
804 tokenbuf_free(result);
808 /* parse class description */
810 parse_class_description(
811 var_t *var, var_parse_t *ctx,
812 tokenbuf_t *src, tokenbuf_t *dst)
818 while (p != src->end) {
819 if ((src->end - p) >= 3 && p[1] == '-') {
821 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
822 for (c = *p, d = p[2]; c <= d; ++c) {
823 if (!tokenbuf_append(dst, (char *)&c, 1))
824 return VAR_ERR_OUT_OF_MEMORY;
828 if (!tokenbuf_append(dst, p, 1))
829 return VAR_ERR_OUT_OF_MEMORY;
836 /* parse regex replace part */
839 var_t *var, var_parse_t *ctx,
843 tokenbuf_t *expanded)
849 tokenbuf_init(expanded);
851 while (p != orig->end) {
853 if (orig->end - p <= 1) {
854 tokenbuf_free(expanded);
855 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
859 if (!tokenbuf_append(expanded, p, 1)) {
860 tokenbuf_free(expanded);
861 return VAR_ERR_OUT_OF_MEMORY;
866 if (!isdigit((int)*p)) {
867 tokenbuf_free(expanded);
868 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
872 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
873 tokenbuf_free(expanded);
874 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
876 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
877 pmatch[i].rm_eo - pmatch[i].rm_so)) {
878 tokenbuf_free(expanded);
879 return VAR_ERR_OUT_OF_MEMORY;
882 if (!tokenbuf_append(expanded, p, 1)) {
883 tokenbuf_free(expanded);
884 return VAR_ERR_OUT_OF_MEMORY;
893 /* operation: transpose */
896 var_t *var, var_parse_t *ctx,
901 tokenbuf_t srcclass, dstclass;
906 tokenbuf_init(&srcclass);
907 tokenbuf_init(&dstclass);
908 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
910 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
912 if (srcclass.begin == srcclass.end) {
913 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
916 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
917 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
920 if (data->buffer_size == 0) {
922 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
923 rc = VAR_ERR_OUT_OF_MEMORY;
926 tokenbuf_move(&tmp, data);
928 for (p = data->begin; p != data->end; ++p) {
929 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
930 if (*p == srcclass.begin[i]) {
931 *((char *)p) = dstclass.begin[i];
936 tokenbuf_free(&srcclass);
937 tokenbuf_free(&dstclass);
941 tokenbuf_free(search);
942 tokenbuf_free(replace);
943 tokenbuf_free(&srcclass);
944 tokenbuf_free(&dstclass);
948 /* operation: search & replace */
950 op_search_and_replace(
951 var_t *var, var_parse_t *ctx,
959 int case_insensitive = 0;
965 if (search->begin == search->end)
966 return VAR_ERR_EMPTY_SEARCH_STRING;
968 for (p = flags->begin; p != flags->end; p++) {
969 switch (tolower(*p)) {
974 case_insensitive = 1;
983 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
988 /* plain text pattern based operation */
990 for (p = data->begin; p != data->end;) {
991 if (case_insensitive)
992 rc = strncasecmp(p, search->begin, search->end - search->begin);
994 rc = strncmp(p, search->begin, search->end - search->begin);
996 /* not matched, copy character */
997 if (!tokenbuf_append(&tmp, p, 1)) {
999 return VAR_ERR_OUT_OF_MEMORY;
1003 /* matched, copy replacement string */
1004 tokenbuf_merge(&tmp, replace);
1005 p += (search->end - search->begin);
1007 /* append remaining text */
1008 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1009 tokenbuf_free(&tmp);
1010 return VAR_ERR_OUT_OF_MEMORY;
1016 tokenbuf_free(data);
1017 tokenbuf_move(&tmp, data);
1019 /* regular expression pattern based operation */
1021 tokenbuf_t myreplace;
1023 regmatch_t pmatch[10];
1026 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1027 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1028 return VAR_ERR_OUT_OF_MEMORY;
1029 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1030 tokenbuf_free(&tmp);
1031 return VAR_ERR_OUT_OF_MEMORY;
1034 /* compile the pattern. */
1035 rc = regcomp(&preg, tmp.begin,
1037 | (multiline ? REG_NEWLINE : 0)
1038 | (case_insensitive ? REG_ICASE : 0)));
1039 tokenbuf_free(&tmp);
1041 tokenbuf_free(&mydata);
1042 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1045 /* match the pattern and create the result string in the tmp buffer */
1046 tokenbuf_append(&tmp, "", 0);
1047 for (p = mydata.begin; p < mydata.end; ) {
1048 if (p == mydata.begin || p[-1] == '\n')
1051 regexec_flag = REG_NOTBOL;
1052 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1054 /* no (more) matching */
1055 tokenbuf_append(&tmp, p, mydata.end - p);
1059 && (p + pmatch[0].rm_so) == mydata.end
1060 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1061 /* special case: found empty pattern (usually /^/ or /$/ only)
1062 in multi-line at end of data (after the last newline) */
1063 tokenbuf_append(&tmp, p, mydata.end - p);
1067 /* append prolog string */
1068 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1070 tokenbuf_free(&tmp);
1071 tokenbuf_free(&mydata);
1072 return VAR_ERR_OUT_OF_MEMORY;
1074 /* create replace string */
1075 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1078 tokenbuf_free(&tmp);
1079 tokenbuf_free(&mydata);
1082 /* append replace string */
1083 if (!tokenbuf_merge(&tmp, &myreplace)) {
1085 tokenbuf_free(&tmp);
1086 tokenbuf_free(&mydata);
1087 tokenbuf_free(&myreplace);
1088 return VAR_ERR_OUT_OF_MEMORY;
1090 tokenbuf_free(&myreplace);
1091 /* skip now processed data */
1092 p += pmatch[0].rm_eo;
1093 /* if pattern matched an empty part (think about
1094 anchor-only regular expressions like /^/ or /$/) we
1095 skip the next character to make sure we do not enter
1096 an infinitive loop in matching */
1097 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1098 if (p >= mydata.end)
1100 if (!tokenbuf_append(&tmp, p, 1)) {
1102 tokenbuf_free(&tmp);
1103 tokenbuf_free(&mydata);
1104 return VAR_ERR_OUT_OF_MEMORY;
1108 /* append prolog string and stop processing if we
1109 do not perform the search & replace globally */
1111 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1113 tokenbuf_free(&tmp);
1114 tokenbuf_free(&mydata);
1115 return VAR_ERR_OUT_OF_MEMORY;
1122 tokenbuf_free(data);
1123 tokenbuf_move(&tmp, data);
1124 tokenbuf_free(&mydata);
1130 /* operation: offset substring */
1133 var_t *var, var_parse_t *ctx,
1142 /* determine begin of result string */
1143 if ((data->end - data->begin) < num1)
1144 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1145 p = data->begin + num1;
1147 /* if num2 is zero, we copy the rest from there. */
1149 if (!tokenbuf_assign(&res, p, data->end - p))
1150 return VAR_ERR_OUT_OF_MEMORY;
1152 /* ok, then use num2. */
1154 if ((p + num2) > data->end)
1155 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1156 if (!tokenbuf_assign(&res, p, num2))
1157 return VAR_ERR_OUT_OF_MEMORY;
1160 return VAR_ERR_OFFSET_LOGIC;
1161 if ((data->begin + num2) > data->end)
1162 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1163 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1164 return VAR_ERR_OUT_OF_MEMORY;
1167 tokenbuf_free(data);
1168 tokenbuf_move(&res, data);
1172 /* operation: padding */
1175 var_t *var, var_parse_t *ctx,
1184 if (fill->begin == fill->end)
1185 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1186 tokenbuf_init(&result);
1187 if (position == 'l') {
1189 i = width - (data->end - data->begin);
1191 i = i / (fill->end - fill->begin);
1193 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1194 return VAR_ERR_OUT_OF_MEMORY;
1197 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1198 if (!tokenbuf_append(data, fill->begin, i))
1199 return VAR_ERR_OUT_OF_MEMORY;
1201 } else if (position == 'r') {
1203 i = width - (data->end - data->begin);
1205 i = i / (fill->end - fill->begin);
1207 if (!tokenbuf_merge(&result, fill)) {
1208 tokenbuf_free(&result);
1209 return VAR_ERR_OUT_OF_MEMORY;
1213 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1214 if (!tokenbuf_append(&result, fill->begin, i)) {
1215 tokenbuf_free(&result);
1216 return VAR_ERR_OUT_OF_MEMORY;
1218 if (!tokenbuf_merge(&result, data)) {
1219 tokenbuf_free(&result);
1220 return VAR_ERR_OUT_OF_MEMORY;
1222 /* move string from temporary buffer to data buffer */
1223 tokenbuf_free(data);
1224 tokenbuf_move(&result, data);
1226 } else if (position == 'c') {
1227 /* centered padding */
1228 i = (width - (data->end - data->begin)) / 2;
1230 /* create the prefix */
1231 i = i / (fill->end - fill->begin);
1233 if (!tokenbuf_merge(&result, fill)) {
1234 tokenbuf_free(&result);
1235 return VAR_ERR_OUT_OF_MEMORY;
1239 i = ((width - (data->end - data->begin)) / 2)
1240 % (fill->end - fill->begin);
1241 if (!tokenbuf_append(&result, fill->begin, i)) {
1242 tokenbuf_free(&result);
1243 return VAR_ERR_OUT_OF_MEMORY;
1245 /* append the actual data string */
1246 if (!tokenbuf_merge(&result, data)) {
1247 tokenbuf_free(&result);
1248 return VAR_ERR_OUT_OF_MEMORY;
1250 /* append the suffix */
1251 i = width - (result.end - result.begin);
1252 i = i / (fill->end - fill->begin);
1254 if (!tokenbuf_merge(&result, fill)) {
1255 tokenbuf_free(&result);
1256 return VAR_ERR_OUT_OF_MEMORY;
1260 i = width - (result.end - result.begin);
1261 if (!tokenbuf_append(&result, fill->begin, i)) {
1262 tokenbuf_free(&result);
1263 return VAR_ERR_OUT_OF_MEMORY;
1265 /* move string from temporary buffer to data buffer */
1266 tokenbuf_free(data);
1267 tokenbuf_move(&result, data);
1273 /* parse an integer number ("123") */
1276 var_t *var, var_parse_t *ctx,
1277 const char *begin, const char *end,
1285 while (isdigit(*p) && p != end) {
1295 /* parse an operation (":x...") */
1298 var_t *var, var_parse_t *ctx,
1299 const char *begin, const char *end,
1303 tokenbuf_t tmptokbuf;
1304 tokenbuf_t search, replace, flags;
1305 tokenbuf_t number1, number2;
1311 /* initialization */
1312 tokenbuf_init(&tmptokbuf);
1313 tokenbuf_init(&search);
1314 tokenbuf_init(&replace);
1315 tokenbuf_init(&flags);
1316 tokenbuf_init(&number1);
1317 tokenbuf_init(&number2);
1322 /* dispatch through the first operation character */
1323 switch (tolower(*p)) {
1325 /* turn value to lowercase. */
1326 if (data->begin != NULL) {
1327 /* if the buffer does not live in an allocated buffer,
1328 we have to copy it before modifying the contents. */
1329 if (data->buffer_size == 0) {
1330 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1331 rc = VAR_ERR_OUT_OF_MEMORY;
1336 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1337 *ptr = (char)tolower((int)(*ptr));
1343 /* turn value to uppercase. */
1344 if (data->begin != NULL) {
1345 /* if the buffer does not live in an allocated buffer,
1346 we have to copy it before modifying the contents. */
1347 if (data->buffer_size == 0) {
1348 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1349 rc = VAR_ERR_OUT_OF_MEMORY;
1354 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1355 *ptr = (char)toupper((int)(*ptr));
1361 /* cut out substring of value. */
1363 rc = parse_integer(var, ctx, p, end, &num1);
1365 rc = VAR_ERR_MISSING_START_OFFSET;
1374 } else if (*p == '-') {
1378 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1381 rc = parse_integer(var, ctx, p, end, &num2);
1383 if (data->begin != NULL) {
1384 rc = op_offset(var, ctx, data, num1, num2, isrange);
1391 /* determine length of the value */
1392 if (data->begin != NULL) {
1393 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1394 sprintf(buf, "%d", (int)(data->end - data->begin));
1395 tokenbuf_free(data);
1396 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1397 rc = VAR_ERR_OUT_OF_MEMORY;
1405 /* substitute parameter if data is empty */
1407 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1411 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1415 if (tokenbuf_isundef(data))
1416 tokenbuf_move(&tmptokbuf, data);
1417 else if (tokenbuf_isempty(data)) {
1418 tokenbuf_free(data);
1419 tokenbuf_move(&tmptokbuf, data);
1424 /* substitute empty string if data is not empty, parameter otherwise. */
1426 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1430 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1434 if (data->begin != NULL) {
1435 if (data->begin == data->end) {
1436 tokenbuf_free(data);
1437 tokenbuf_move(&tmptokbuf, data);
1439 tokenbuf_free(data);
1440 data->begin = data->end = "";
1441 data->buffer_size = 0;
1447 /* substitute parameter if data is not empty. */
1449 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1453 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1457 if (data->begin != NULL && data->begin != data->end) {
1458 tokenbuf_free(data);
1459 tokenbuf_move(&tmptokbuf, data);
1464 /* search and replace. */
1467 return VAR_ERR_MALFORMATTED_REPLACE;
1469 rc = parse_pattern(var, ctx, p, end);
1472 tokenbuf_set(&search, p, p + rc, 0);
1475 rc = VAR_ERR_MALFORMATTED_REPLACE;
1479 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1484 rc = VAR_ERR_MALFORMATTED_REPLACE;
1488 rc = parse_exptext(var, ctx, p, end);
1491 tokenbuf_set(&flags, p, p + rc, 0);
1493 if (data->begin != NULL) {
1494 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1501 /* transpose characters from class A to class B. */
1504 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1506 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1511 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1515 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1520 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1525 rc = op_transpose(var, ctx, data, &search, &replace);
1535 return VAR_ERR_MALFORMATTED_PADDING;
1537 rc = parse_integer(var, ctx, p, end, &num1);
1539 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1544 rc = VAR_ERR_MALFORMATTED_PADDING;
1548 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1553 rc = VAR_ERR_MALFORMATTED_PADDING;
1557 if (*p != 'l' && *p != 'c' && *p != 'r') {
1558 rc = VAR_ERR_MALFORMATTED_PADDING;
1563 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1570 /* operation callback function */
1573 const char *arg_ptr;
1575 const char *val_ptr;
1577 const char *out_ptr;
1583 rc = parse_name(var, ctx, p, end);
1591 tokenbuf_init(&args);
1592 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1596 arg_ptr = args.begin;
1597 arg_len = args.end - args.begin;
1599 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1608 val_ptr = data->begin;
1609 val_len = data->end - data->begin;
1611 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1612 /* call operation callback function */
1613 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1617 &out_ptr, &out_len, &out_size);
1619 if (arg_ptr != NULL)
1620 free((void *)arg_ptr);
1623 tokenbuf_free(data);
1624 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1626 if (arg_ptr != NULL)
1627 free((void *)arg_ptr);
1631 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1634 /* return successfully */
1635 tokenbuf_free(&tmptokbuf);
1636 tokenbuf_free(&search);
1637 tokenbuf_free(&replace);
1638 tokenbuf_free(&flags);
1639 tokenbuf_free(&number1);
1640 tokenbuf_free(&number2);
1643 /* return with an error */
1645 tokenbuf_free(data);
1646 tokenbuf_free(&tmptokbuf);
1647 tokenbuf_free(&search);
1648 tokenbuf_free(&replace);
1649 tokenbuf_free(&flags);
1650 tokenbuf_free(&number1);
1651 tokenbuf_free(&number2);
1655 /* parse numerical expression operand */
1657 parse_numexp_operand(
1658 var_t *var, var_parse_t *ctx,
1659 const char *begin, const char *end,
1660 int *result, int *failed)
1667 /* initialization */
1669 tokenbuf_init(&tmp);
1671 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1673 /* parse opening numerical expression */
1675 /* parse inner numerical expression */
1676 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1681 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1682 /* parse closing parenthesis */
1684 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1687 /* parse contained variable */
1688 else if (*p == var->syntax.delim_init) {
1689 /* parse variable with forced expansion */
1690 ctx = var_parse_push(ctx, &myctx);
1691 ctx->force_expand = 1;
1692 rc = parse_variable(var, ctx, p, end, &tmp);
1693 ctx = var_parse_pop(ctx);
1695 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1697 /* parse variable without forced expansion */
1698 ctx = var_parse_push(ctx, &myctx);
1699 ctx->force_expand = 0;
1700 rc = parse_variable(var, ctx, p, end, &tmp);
1701 ctx = var_parse_pop(ctx);
1706 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1707 } else if (rc < 0) {
1711 /* parse remaining numerical expression */
1712 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1713 tokenbuf_free(&tmp);
1718 /* parse relative index mark ("#") */
1719 else if ( var->syntax.index_mark != EOS
1720 && *p == var->syntax.index_mark) {
1722 *result = ctx->index_this;
1723 if (ctx->rel_lookup_flag)
1724 ctx->rel_lookup_cnt++;
1726 /* parse plain integer number */
1727 else if (isdigit(*p)) {
1728 rc = parse_integer(var, ctx, p, end, result);
1731 /* parse signed positive integer number */
1732 else if (*p == '+') {
1733 if ((end - p) > 1 && isdigit(p[1])) {
1735 rc = parse_integer(var, ctx, p, end, result);
1739 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1741 /* parse signed negative integer number */
1742 else if (*p == '-') {
1743 if (end - p > 1 && isdigit(p[1])) {
1745 rc = parse_integer(var, ctx, p, end, result);
1746 *result = -(*result);
1750 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1752 /* else we failed to parse anything reasonable */
1754 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1759 /* parse numerical expression ("x+y") */
1762 var_t *var, var_parse_t *ctx,
1763 const char *begin, const char *end,
1764 int *result, int *failed)
1771 /* initialization */
1774 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1776 /* parse left numerical operand */
1777 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1782 /* parse numerical operator */
1784 if (*p == '+' || *p == '-') {
1786 /* recursively parse right operand (light binding) */
1787 rc = parse_numexp(var, ctx, p, end, &right, failed);
1792 *result = (*result + right);
1794 *result = (*result - right);
1796 else if (*p == '*' || *p == '/' || *p == '%') {
1798 /* recursively parse right operand (string binding) */
1799 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1804 *result = (*result * right);
1805 else if (op == '/') {
1810 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1813 *result = (*result / right);
1815 else if (op == '%') {
1820 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1823 *result = (*result % right);
1830 /* return amount of parsed input */
1834 /* parse variable name ("abc") */
1837 var_t *var, var_parse_t *ctx,
1838 const char *begin, const char *end)
1842 /* parse as long as name class characters are found */
1843 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1848 /* lookup a variable value through the callback function */
1851 var_t *var, var_parse_t *ctx,
1852 const char *var_ptr, int var_len, int var_inc, int var_idx,
1853 const char **val_ptr, int *val_len, int *val_size)
1858 /* pass through to original callback */
1859 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1860 var_ptr, var_len, var_inc, var_idx,
1861 val_ptr, val_len, val_size);
1863 /* convert undefined variable into empty variable if relative
1864 lookups are counted. This is the case inside an active loop
1865 construct if no limits are given. There the parse_input()
1866 has to proceed until all variables have undefined values.
1867 This trick here allows it to determine this case. */
1868 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1869 ctx->rel_lookup_cnt--;
1880 /* parse complex variable construct ("${name...}") */
1882 parse_variable_complex(
1883 var_t *var, var_parse_t *ctx,
1884 const char *begin, const char *end,
1889 int len, buffer_size;
1897 /* initializations */
1899 tokenbuf_init(&name);
1900 tokenbuf_init(&tmp);
1901 tokenbuf_init(result);
1903 /* parse open delimiter */
1904 if (p == end || *p != var->syntax.delim_open)
1908 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1910 /* parse name of variable to expand. The name may consist of an
1911 arbitrary number of variable name character and contained variable
1914 /* parse a variable name */
1915 rc = parse_name(var, ctx, p, end);
1919 if (!tokenbuf_append(&name, p, rc)) {
1920 rc = VAR_ERR_OUT_OF_MEMORY;
1926 /* parse an (embedded) variable */
1927 rc = parse_variable(var, ctx, p, end, &tmp);
1931 if (!tokenbuf_merge(&name, &tmp)) {
1932 rc = VAR_ERR_OUT_OF_MEMORY;
1937 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1940 /* we must have the complete expanded variable name now,
1941 so make sure we really do. */
1942 if (name.begin == name.end) {
1943 if (ctx->force_expand) {
1944 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1948 /* If no force_expand is requested, we have to back-off.
1949 We're not sure whether our approach here is 100% correct,
1950 because it _could_ have side-effects according to Peter
1951 Simons, but as far as we know and tried it, it is
1952 correct. But be warned -- RSE */
1953 tokenbuf_set(result, begin - 1, p, 0);
1958 /* parse an optional index specification */
1959 if ( var->syntax.index_open != EOS
1960 && *p == var->syntax.index_open) {
1962 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1966 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1971 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1974 if (*p != var->syntax.index_close) {
1975 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1981 /* parse end of variable construct or start of post-operations */
1982 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1983 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1986 inc = (*p == '+'); /* increment variable */
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));
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 */
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];
2721 #endif /* !defined(HAVE_WIN32) */