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-2003 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,
42 #if defined(HAVE_PCREPOSIX)
43 # include <pcreposix.h>
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];
123 if (format == NULL || ap == NULL)
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;
207 if (format == NULL || ap == NULL)
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 (c >= 'a' && c <= 'f')
518 c = **src - 'a' + 10;
519 else if (c >= 'A' && c <= '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);
739 (result, tmp.begin, tmp.end - tmp.begin)) {
740 rc = VAR_ERR_OUT_OF_MEMORY;
744 tokenbuf_free(&tmp); /* KES 11/9/2003 */
752 tokenbuf_free(result);
756 /* parse substitution text or variable */
758 parse_substext_or_variable(
759 var_t *var, var_parse_t *ctx,
760 const char *begin, const char *end,
763 const char *p = begin;
767 tokenbuf_init(result);
772 /* try to parse substitution text */
773 rc = parse_substext(var, ctx, p, end);
777 if (!tokenbuf_append(result, p, rc)) {
778 rc = VAR_ERR_OUT_OF_MEMORY;
784 /* try to parse substitution text */
785 rc = parse_variable(var, ctx, p, end, &tmp);
791 (result, tmp.begin, tmp.end - tmp.begin)) {
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_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) {
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_append(&result, fill->begin, fill->end - fill->begin)) {
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_append(&result, data->begin, data->end - data->begin)) {
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_append(&result, fill->begin, fill->end - fill->begin)) {
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_append(&result, data->begin, data->end - data->begin)) {
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_append(&result, fill->begin, fill->end - fill->begin)) {
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);
1620 tokenbuf_free(data);
1621 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1626 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1629 /* return successfully */
1630 tokenbuf_free(&tmptokbuf);
1631 tokenbuf_free(&search);
1632 tokenbuf_free(&replace);
1633 tokenbuf_free(&flags);
1634 tokenbuf_free(&number1);
1635 tokenbuf_free(&number2);
1638 /* return with an error */
1640 tokenbuf_free(data);
1641 tokenbuf_free(&tmptokbuf);
1642 tokenbuf_free(&search);
1643 tokenbuf_free(&replace);
1644 tokenbuf_free(&flags);
1645 tokenbuf_free(&number1);
1646 tokenbuf_free(&number2);
1650 /* parse numerical expression operand */
1652 parse_numexp_operand(
1653 var_t *var, var_parse_t *ctx,
1654 const char *begin, const char *end,
1655 int *result, int *failed)
1662 /* initialization */
1664 tokenbuf_init(&tmp);
1666 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1668 /* parse opening numerical expression */
1670 /* parse inner numerical expression */
1671 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1676 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1677 /* parse closing parenthesis */
1679 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1682 /* parse contained variable */
1683 else if (*p == var->syntax.delim_init) {
1684 /* parse variable with forced expansion */
1685 ctx = var_parse_push(ctx, &myctx);
1686 ctx->force_expand = 1;
1687 rc = parse_variable(var, ctx, p, end, &tmp);
1688 ctx = var_parse_pop(ctx);
1690 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1692 /* parse variable without forced expansion */
1693 ctx = var_parse_push(ctx, &myctx);
1694 ctx->force_expand = 0;
1695 rc = parse_variable(var, ctx, p, end, &tmp);
1696 ctx = var_parse_pop(ctx);
1701 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1702 } else if (rc < 0) {
1706 /* parse remaining numerical expression */
1707 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1708 tokenbuf_free(&tmp);
1713 /* parse relative index mark ("#") */
1714 else if ( var->syntax.index_mark != EOS
1715 && *p == var->syntax.index_mark) {
1717 *result = ctx->index_this;
1718 if (ctx->rel_lookup_flag)
1719 ctx->rel_lookup_cnt++;
1721 /* parse plain integer number */
1722 else if (isdigit(*p)) {
1723 rc = parse_integer(var, ctx, p, end, result);
1726 /* parse signed positive integer number */
1727 else if (*p == '+') {
1728 if ((end - p) > 1 && isdigit(p[1])) {
1730 rc = parse_integer(var, ctx, p, end, result);
1734 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1736 /* parse signed negative integer number */
1737 else if (*p == '-') {
1738 if (end - p > 1 && isdigit(p[1])) {
1740 rc = parse_integer(var, ctx, p, end, result);
1741 *result = -(*result);
1745 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1747 /* else we failed to parse anything reasonable */
1749 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1754 /* parse numerical expression ("x+y") */
1757 var_t *var, var_parse_t *ctx,
1758 const char *begin, const char *end,
1759 int *result, int *failed)
1766 /* initialization */
1769 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1771 /* parse left numerical operand */
1772 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1777 /* parse numerical operator */
1779 if (*p == '+' || *p == '-') {
1781 /* recursively parse right operand (light binding) */
1782 rc = parse_numexp(var, ctx, p, end, &right, failed);
1787 *result = (*result + right);
1789 *result = (*result - right);
1791 else if (*p == '*' || *p == '/' || *p == '%') {
1793 /* recursively parse right operand (string binding) */
1794 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1799 *result = (*result * right);
1800 else if (op == '/') {
1805 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1808 *result = (*result / right);
1810 else if (op == '%') {
1815 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1818 *result = (*result % right);
1825 /* return amount of parsed input */
1829 /* parse variable name ("abc") */
1832 var_t *var, var_parse_t *ctx,
1833 const char *begin, const char *end)
1837 /* parse as long as name class characters are found */
1838 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1843 /* lookup a variable value through the callback function */
1846 var_t *var, var_parse_t *ctx,
1847 const char *var_ptr, int var_len, int var_inc, int var_idx,
1848 const char **val_ptr, int *val_len, int *val_size)
1853 /* pass through to original callback */
1854 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1855 var_ptr, var_len, var_inc, var_idx,
1856 val_ptr, val_len, val_size);
1858 /* convert undefined variable into empty variable if relative
1859 lookups are counted. This is the case inside an active loop
1860 construct if no limits are given. There the parse_input()
1861 has to proceed until all variables have undefined values.
1862 This trick here allows it to determine this case. */
1863 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1864 ctx->rel_lookup_cnt--;
1875 /* parse complex variable construct ("${name...}") */
1877 parse_variable_complex(
1878 var_t *var, var_parse_t *ctx,
1879 const char *begin, const char *end,
1884 int len, buffer_size;
1892 /* initializations */
1894 tokenbuf_init(&name);
1895 tokenbuf_init(&tmp);
1896 tokenbuf_init(result);
1898 /* parse open delimiter */
1899 if (p == end || *p != var->syntax.delim_open)
1903 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1905 /* parse name of variable to expand. The name may consist of an
1906 arbitrary number of variable name character and contained variable
1909 /* parse a variable name */
1910 rc = parse_name(var, ctx, p, end);
1914 if (!tokenbuf_append(&name, p, rc)) {
1915 rc = VAR_ERR_OUT_OF_MEMORY;
1921 /* parse an (embedded) variable */
1922 rc = parse_variable(var, ctx, p, end, &tmp);
1926 if (!tokenbuf_merge(&name, &tmp)) {
1927 rc = VAR_ERR_OUT_OF_MEMORY;
1932 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1935 /* we must have the complete expanded variable name now,
1936 so make sure we really do. */
1937 if (name.begin == name.end) {
1938 if (ctx->force_expand) {
1939 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1943 /* If no force_expand is requested, we have to back-off.
1944 We're not sure whether our approach here is 100% correct,
1945 because it _could_ have side-effects according to Peter
1946 Simons, but as far as we know and tried it, it is
1947 correct. But be warned -- RSE */
1948 tokenbuf_set(result, begin - 1, p, 0);
1953 /* parse an optional index specification */
1954 if ( var->syntax.index_open != EOS
1955 && *p == var->syntax.index_open) {
1957 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1961 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1966 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1969 if (*p != var->syntax.index_close) {
1970 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1976 /* parse end of variable construct or start of post-operations */
1977 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1978 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1981 inc = (*p == '+'); /* increment variable */
1984 /* lookup the variable value now */
1986 tokenbuf_set(result, begin - 1, p, 0);
1988 rc = lookup_value(var, ctx,
1989 name.begin, name.end-name.begin, inc, idx,
1990 &data, &len, &buffer_size);
1991 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1992 tokenbuf_init(result); /* delayed handling of undefined variable */
1993 } else if (rc < 0) {
1996 /* the preliminary result is the raw value of the variable.
1997 This may be modified by the operations that may follow. */
1998 tokenbuf_set(result, data, data + len, buffer_size);
2002 /* parse optional post-operations */
2005 tokenbuf_free(&tmp);
2006 tokenbuf_init(&tmp);
2008 while (p != end && *p == ':') {
2011 rc = parse_operation(var, ctx, p, end, result);
2013 rc = parse_operation(var, ctx, p, end, &tmp);
2020 if (p == end || *p != var->syntax.delim_close) {
2021 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2027 } else if (p[-1] == '+') {
2031 /* lazy handling of undefined variable */
2032 if (!failed && tokenbuf_isundef(result)) {
2033 if (ctx->force_expand) {
2034 rc = VAR_ERR_UNDEFINED_VARIABLE;
2037 tokenbuf_set(result, begin - 1, p, 0);
2041 /* return successfully */
2042 tokenbuf_free(&name);
2043 tokenbuf_free(&tmp);
2046 /* return with an error */
2048 tokenbuf_free(&name);
2049 tokenbuf_free(&tmp);
2050 tokenbuf_free(result);
2054 /* parse variable construct ("$name" or "${name...}") */
2057 var_t *var, var_parse_t *ctx,
2058 const char *begin, const char *end,
2063 int len, buffer_size;
2067 /* initialization */
2069 tokenbuf_init(result);
2071 /* parse init delimiter */
2072 if (p == end || *p != var->syntax.delim_init)
2076 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2078 /* parse a simple variable name.
2079 (if this fails, we're try to parse a complex variable construct) */
2080 rc = parse_name(var, ctx, p, end);
2084 inc = (p[rc] == '+');
2085 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2086 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2087 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2092 tokenbuf_set(result, data, data + len, buffer_size);
2096 /* parse a complex variable construct (else case) */
2097 rc = parse_variable_complex(var, ctx, p, end, result);
2103 /* parse loop construct limits ("[...]{b,s,e}") */
2106 var_t *var, var_parse_t *ctx,
2107 const char *begin, const char *end,
2108 int *start, int *step, int *stop, int *open_stop)
2114 /* initialization */
2117 /* we are happy if nothing is to left to parse */
2121 /* parse start delimiter */
2122 if (*p != var->syntax.delim_open)
2126 /* parse loop start value */
2128 rc = parse_numexp(var, ctx, p, end, start, &failed);
2129 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2130 *start = 0; /* use default */
2132 return (var_rc_t)rc;
2136 return VAR_ERR_UNDEFINED_VARIABLE;
2138 /* parse separator */
2140 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2143 /* parse loop step value */
2145 rc = parse_numexp(var, ctx, p, end, step, &failed);
2146 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2147 *step = 1; /* use default */
2149 return (var_rc_t)rc;
2153 return VAR_ERR_UNDEFINED_VARIABLE;
2155 /* parse separator */
2157 /* if not found, parse end delimiter */
2158 if (*p != var->syntax.delim_close)
2159 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2162 /* shift step value to stop value */
2166 /* determine whether loop end is open */
2171 return (var_rc_t)(p - begin);
2175 /* parse loop stop value */
2177 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2178 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2179 *stop = 0; /* use default */
2183 return (var_rc_t)rc;
2189 return VAR_ERR_UNDEFINED_VARIABLE;
2191 /* parse end delimiter */
2192 if (*p != var->syntax.delim_close)
2193 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2196 /* return amount of parsed input */
2197 return (var_rc_t)(p - begin);
2200 /* parse plain text */
2203 var_t *var, var_parse_t *ctx,
2204 const char *begin, const char *end)
2208 /* parse until delim_init (variable construct)
2209 or index_open (loop construct) is found */
2210 for (p = begin; p != end; p++) {
2211 if (*p == var->syntax.escape) {
2212 p++; /* skip next character */
2214 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2216 else if (*p == var->syntax.delim_init)
2218 else if ( var->syntax.index_open != EOS
2219 && ( *p == var->syntax.index_open
2220 || *p == var->syntax.index_close))
2226 /* expand input in general */
2229 var_t *var, var_parse_t *ctx,
2230 const char *begin, const char *end,
2231 tokenbuf_t *output, int recursion_level)
2236 int start, step, stop, open_stop;
2240 int loop_limit_length;
2243 /* initialization */
2247 /* try to parse a loop construct */
2249 && var->syntax.index_open != EOS
2250 && *p == var->syntax.index_open) {
2253 /* loop preparation */
2254 loop_limit_length = -1;
2255 rel_lookup_cnt = ctx->rel_lookup_cnt;
2263 /* iterate over loop construct, either as long as there is
2264 (still) nothing known about the limit, or there is an open
2265 (=unknown) limit stop and there are still defined variables
2266 or there is a stop limit known and it is still not reached */
2270 && ( loop_limit_length < 0
2271 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2276 /* remember current output end for restoring */
2277 output_backup = (output->end - output->begin);
2279 /* open temporary context for recursion */
2280 ctx = var_parse_push(ctx, &myctx);
2281 ctx->force_expand = 1;
2282 ctx->rel_lookup_flag = 1;
2283 ctx->index_this = i;
2285 /* recursive parse input through ourself */
2286 rc = parse_input(var, ctx, p, end,
2287 output, recursion_level+1);
2289 /* retrieve info and close temporary context */
2290 rel_lookup_cnt = ctx->rel_lookup_cnt;
2291 ctx = var_parse_pop(ctx);
2293 /* error handling */
2297 /* make sure the loop construct is closed */
2298 if (p[rc] != var->syntax.index_close) {
2299 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2303 /* try to parse loop construct limit specification */
2304 if (loop_limit_length < 0) {
2305 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2306 &start, &step, &stop, &open_stop);
2310 loop_limit_length = 0;
2312 loop_limit_length = rc2;
2313 /* restart loop from scratch */
2314 output->end = (output->begin + output_backup);
2320 /* if stop value is open, restore to the output end
2321 because the last iteration was just to determine the loop
2322 termination and its result has to be discarded */
2324 output->end = (output->begin + output_backup);
2326 /* skip parsed loop construct */
2329 p += loop_limit_length;
2334 /* try to parse plain text */
2335 rc = parse_text(var, ctx, p, end);
2337 if (!tokenbuf_append(output, p, rc)) {
2338 rc = VAR_ERR_OUT_OF_MEMORY;
2346 /* try to parse a variable construct */
2347 tokenbuf_init(&result);
2348 rc = parse_variable(var, ctx, p, end, &result);
2350 if (!tokenbuf_merge(output, &result)) {
2351 tokenbuf_free(&result);
2352 rc = VAR_ERR_OUT_OF_MEMORY;
2355 tokenbuf_free(&result);
2359 tokenbuf_free(&result);
2363 } while (p != end && rc > 0);
2365 /* We do not know whether this really could happen, but because we
2366 are paranoid, report an error at the outer most parsing level if
2367 there is still any input. Because this would mean that we are no
2368 longer able to parse the remaining input as a loop construct, a
2369 text or a variable construct. This would be very strange, but
2370 could perhaps happen in case of configuration errors!?... */
2371 if (recursion_level == 0 && p != end) {
2372 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2376 /* return amount of parsed text */
2377 return (var_rc_t)(p - begin);
2379 /* return with an error where as a special case the output begin is
2380 set to the input begin and the output end to the last input parsing
2383 tokenbuf_free(output);
2384 tokenbuf_set(output, begin, p, 0);
2385 return (var_rc_t)rc;
2390 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2394 /* create variable expansion context */
2402 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2403 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2404 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2405 memset(var, 0, sizeof(var));
2406 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2411 /* destroy variable expansion context */
2417 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2422 /* configure variable expansion context */
2433 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2436 case VAR_CONFIG_SYNTAX: {
2438 s = (var_syntax_t *)va_arg(ap, void *);
2440 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2441 var->syntax.escape = s->escape;
2442 var->syntax.delim_init = s->delim_init;
2443 var->syntax.delim_open = s->delim_open;
2444 var->syntax.delim_close = s->delim_close;
2445 var->syntax.index_open = s->index_open;
2446 var->syntax.index_close = s->index_close;
2447 var->syntax.index_mark = s->index_mark;
2448 var->syntax.name_chars = NULL; /* unused internally */
2449 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2451 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2452 || var->syntax_nameclass[(int)var->syntax.delim_open]
2453 || var->syntax_nameclass[(int)var->syntax.delim_close]
2454 || var->syntax_nameclass[(int)var->syntax.escape])
2455 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2458 case VAR_CONFIG_CB_VALUE: {
2461 fct = (var_cb_value_t)va_arg(ap, void *);
2462 ctx = (void *)va_arg(ap, void *);
2463 var->cb_value_fct = fct;
2464 var->cb_value_ctx = ctx;
2467 case VAR_CONFIG_CB_OPERATION: {
2468 var_cb_operation_t fct;
2470 fct = (var_cb_operation_t)va_arg(ap, void *);
2471 ctx = (void *)va_arg(ap, void *);
2472 var->cb_operation_fct = fct;
2473 var->cb_operation_ctx = ctx;
2477 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2483 /* perform unescape operation on a buffer */
2487 const char *src, int srclen,
2488 char *dst, int dstlen,
2494 if (var == NULL || src == NULL || dst == NULL)
2495 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2500 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2519 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2522 case '0': case '1': case '2': case '3': case '4':
2523 case '5': case '6': case '7': case '8': case '9':
2525 && isdigit((int)src[1])
2526 && isdigit((int)src[2])) {
2527 if ((rc = expand_octal(&src, &dst, end)) != 0)
2545 /* perform expand operation on a buffer */
2549 const char *src_ptr, int src_len,
2550 char **dst_ptr, int *dst_len,
2557 /* argument sanity checks */
2558 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2559 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2561 /* prepare internal expansion context */
2563 ctx.force_expand = force_expand;
2564 ctx.rel_lookup_flag = 0;
2565 ctx.rel_lookup_cnt = 0;
2568 /* start the parsing */
2569 tokenbuf_init(&output);
2570 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2572 /* post-processing */
2574 /* always EOS-terminate output for convinience reasons
2575 but do not count the EOS-terminator in the length */
2576 if (!tokenbuf_append(&output, "\0", 1)) {
2577 tokenbuf_free(&output);
2578 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2582 /* provide result */
2583 *dst_ptr = (char *)output.begin;
2584 if (dst_len != NULL)
2585 *dst_len = (output.end - output.begin);
2589 /* provide result */
2590 if (dst_len != NULL)
2591 *dst_len = (output.end - output.begin);
2597 /* format and expand a string */
2601 char **dst_ptr, int force_expand,
2602 const char *fmt, va_list ap)
2609 /* argument sanity checks */
2610 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2611 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2613 /* determine formatting buffer length */
2615 nBuf = var_mvsnprintf(NULL, 0, fmt, ap);
2618 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2620 /* perform formatting */
2621 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2622 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2623 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2626 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2629 /* perform expansion */
2630 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2641 /* format and expand a string */
2645 char **dst_ptr, int force_expand,
2646 const char *fmt, ...)
2651 /* argument sanity checks */
2652 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2653 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2656 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2662 /* var_rc_t to string mapping table */
2663 static const char *var_errors[] = {
2664 "everything ok", /* VAR_OK = 0 */
2665 "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2666 "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */
2667 "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */
2668 "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */
2669 "invalid octal value", /* VAR_ERR_INVALID_OCTAL */
2670 "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */
2671 "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2672 "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */
2673 "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */
2674 "out of memory", /* VAR_ERR_OUT_OF_MEMORY */
2675 "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2676 "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */
2677 "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2678 "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2679 "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */
2680 "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2681 "invalid regex in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2682 "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2683 "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */
2684 "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */
2685 "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2686 "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2687 "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2688 "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */
2689 "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2690 "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2691 "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2692 "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2693 "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */
2694 "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */
2695 "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2696 "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2697 "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2698 "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */
2699 "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2700 "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2701 "index of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2702 "index of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2703 "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2704 "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2705 "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2706 "invalid character in loop limits", /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2707 "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2708 "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */
2709 "formatting failure" /* VAR_ERR_FORMATTING_FAILURE */
2712 /* translate a return code into its corresponding descriptive text */
2713 char *var_strerror(var_t *var, var_rc_t rc)
2716 rc = (var_rc_t)(0 - rc);
2717 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2718 str = "unknown error";
2720 str = (char *)var_errors[rc];