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,
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];
126 while (*format != '\0') {
127 if (*format == '%') {
136 c = (char)va_arg(ap, int);
142 if ((cp = (char *)va_arg(ap, char *)) == NULL)
148 d = (int)va_arg(ap, int);
149 bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
163 if ((format = strchr(cp, '%')) == NULL)
164 format = strchr(cp, '\0');
167 /* perform output operation */
169 if ((n = output(ctx, cp, n)) == -1)
176 /* output callback function context for var_mvsnprintf() */
180 } var_mvsnprintf_cb_t;
182 /* output callback function for var_mvsnprintf() */
186 const char *buffer, int bufsize)
188 var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
190 if (bufsize > ctx->buflen)
192 memcpy(ctx->bufptr, buffer, bufsize);
193 ctx->bufptr += bufsize;
194 ctx->buflen -= bufsize;
198 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
201 char *buffer, int bufsize,
202 const char *format, va_list ap)
205 var_mvsnprintf_cb_t ctx;
209 if (buffer != NULL && bufsize == 0)
212 /* just determine output length */
213 n = var_mvxprintf(NULL, NULL, format, ap);
215 /* perform real output */
217 ctx.buflen = bufsize;
218 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
219 if (n != -1 && ctx.buflen == 0)
222 *(ctx.bufptr) = '\0';
229 ** ==== PARSE CONTEXT FUNCTIONS ====
235 var_parse_t *lower, var_parse_t *upper)
239 memcpy(upper, lower, sizeof(var_parse_t));
240 upper->lower = lower;
255 ** ==== TOKEN BUFFER FUNCTIONS ====
259 #define TOKENBUF_INITIAL_BUFSIZE 64
273 buf->buffer_size = 0;
281 if (buf->begin == NULL && buf->end == NULL)
290 if (buf->begin == buf->end)
297 tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
301 buf->buffer_size = buffer_size;
307 tokenbuf_t *src, tokenbuf_t *dst)
309 dst->begin = src->begin;
311 dst->buffer_size = src->buffer_size;
318 tokenbuf_t *buf, const char *data, int len)
322 if ((p = (char *)malloc(len + 1)) == NULL)
324 memcpy(p, data, len);
327 buf->buffer_size = len + 1;
328 *((char *)(buf->end)) = EOS;
334 tokenbuf_t *output, const char *data, int len)
340 /* Is the tokenbuffer initialized at all? If not, allocate a
341 standard-sized buffer to begin with. */
342 if (output->begin == NULL) {
343 if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
345 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
348 /* does the token contain text, but no buffer has been allocated yet? */
349 if (output->buffer_size == 0) {
350 /* check whether data borders to output. If, we can append
351 simly by increasing the end pointer. */
352 if (output->end == data) {
356 /* ok, so copy the contents of output into an allocated buffer
357 so that we can append that way. */
358 if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
360 memcpy(tmp, output->begin, output->end - output->begin);
361 output->buffer_size = output->end - output->begin;
363 output->end = tmp + output->buffer_size;
364 output->buffer_size += len + 1;
367 /* does the token fit into the current buffer? If not, realloc a
368 larger buffer that fits. */
369 if ((output->buffer_size - (output->end - output->begin)) <= len) {
370 new_size = output->buffer_size;
373 } while ((new_size - (output->end - output->begin)) <= len);
374 if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
376 output->end = new_buffer + (output->end - output->begin);
377 output->begin = new_buffer;
378 output->buffer_size = new_size;
381 /* append the data at the end of the current buffer. */
383 memcpy((char *)output->end, data, len);
385 *((char *)output->end) = EOS;
391 tokenbuf_t *output, tokenbuf_t *input)
393 return tokenbuf_append(output, input->begin, input->end - input->begin);
400 if (buf->begin != NULL && buf->buffer_size > 0)
401 free((char *)buf->begin);
402 buf->begin = buf->end = NULL;
403 buf->buffer_size = 0;
409 ** ==== CHARACTER CLASS EXPANSION ====
414 expand_range(char a, char b, char_class_t chrclass)
417 chrclass[(int)a] = 1;
423 expand_character_class(const char *desc, char_class_t chrclass)
427 /* clear the class array. */
428 for (i = 0; i < 256; ++i)
431 /* walk through class description and set appropriate entries in array */
432 while (*desc != EOS) {
433 if (desc[1] == '-' && desc[2] != EOS) {
434 if (desc[0] > desc[2])
435 return VAR_ERR_INCORRECT_CLASS_SPEC;
436 expand_range(desc[0], desc[2], chrclass);
439 chrclass[(int) *desc] = 1;
448 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
456 if (c >= '0' && c <= '7')
464 const char **src, char **dst, const char *end)
469 return VAR_ERR_INCOMPLETE_OCTAL;
470 if ( !expand_isoct(**src)
471 || !expand_isoct((*src)[1])
472 || !expand_isoct((*src)[2]))
473 return VAR_ERR_INVALID_OCTAL;
477 return VAR_ERR_OCTAL_TOO_LARGE;
496 if ((c >= '0' && c <= '9') ||
497 (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
505 const char **src, char **dst, const char *end)
510 return VAR_ERR_INCOMPLETE_HEX;
511 if ( !expand_ishex(**src)
512 || !expand_ishex((*src)[1]))
513 return VAR_ERR_INVALID_HEX;
515 if (**src >= '0' && **src <= '9')
517 else if (**src >= 'a' && **src <= 'f')
518 c = **src - 'a' + 10;
519 else if (**src >= 'A' && **src <= 'F')
520 c = **src - 'A' + 10;
525 if (**src >= '0' && **src <= '9')
527 else if (**src >= 'a' && **src <= 'f')
528 c += **src - 'a' + 10;
529 else if (**src >= 'A' && **src <= 'F')
530 c += **src - 'A' + 10;
539 const char **src, char **dst, const char *end)
543 while (*src < end && **src != '}') {
544 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
549 return VAR_ERR_INCOMPLETE_GROUPED_HEX;
556 const char **src, char **dst, const char *end)
559 return VAR_ERR_INCOMPLETE_HEX;
562 return expand_grouped_hex(src, dst, end);
564 return expand_simple_hex(src, dst, end);
569 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
573 /* forward declarations */
574 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
575 static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
576 static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
578 /* parse pattern text */
581 var_t *var, var_parse_t *ctx,
582 const char *begin, const char *end)
586 /* parse until '/' */
587 for (p = begin; p != end && *p != '/'; p++) {
588 if (*p == var->syntax.escape) {
590 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
597 /* parse substitution text */
600 var_t *var, var_parse_t *ctx,
601 const char *begin, const char *end)
605 /* parse until delim_init or '/' */
606 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
607 if (*p == var->syntax.escape) {
609 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
616 /* parse expression text */
619 var_t *var, var_parse_t *ctx,
620 const char *begin, const char *end)
624 /* parse until delim_init or delim_close or ':' */
625 for (p = begin; p != end
626 && *p != var->syntax.delim_init
627 && *p != var->syntax.delim_close
629 if (*p == var->syntax.escape) {
631 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
638 /* parse opertion argument text */
641 var_t *var, var_parse_t *ctx,
642 const char *begin, const char *end)
646 /* parse until delim_init or ')' */
647 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
648 if (*p == var->syntax.escape) {
650 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
658 parse_opargtext_or_variable(
659 var_t *var, var_parse_t *ctx,
660 const char *begin, const char *end,
667 tokenbuf_init(result);
673 rc = parse_opargtext(var, ctx, p, end);
677 if (!tokenbuf_append(result, p, rc)) {
678 rc = VAR_ERR_OUT_OF_MEMORY;
683 rc = parse_variable(var, ctx, p, end, &tmp);
688 if (!tokenbuf_merge(result, &tmp)) {
689 rc = VAR_ERR_OUT_OF_MEMORY;
693 tokenbuf_free(&tmp); /* KES 11/9/2003 */
700 tokenbuf_free(result);
704 /* parse expression or variable */
706 parse_exptext_or_variable(
707 var_t *var, var_parse_t *ctx,
708 const char *begin, const char *end,
711 const char *p = begin;
715 tokenbuf_init(result);
720 /* try to parse expression text */
721 rc = parse_exptext(var, ctx, p, end);
725 if (!tokenbuf_append(result, p, rc)) {
726 rc = VAR_ERR_OUT_OF_MEMORY;
732 /* try to parse variable construct */
733 rc = parse_variable(var, ctx, p, end, &tmp);
738 if (!tokenbuf_merge(result, &tmp)) {
739 rc = VAR_ERR_OUT_OF_MEMORY;
743 tokenbuf_free(&tmp); /* KES 11/9/2003 */
751 tokenbuf_free(result);
755 /* parse substitution text or variable */
757 parse_substext_or_variable(
758 var_t *var, var_parse_t *ctx,
759 const char *begin, const char *end,
762 const char *p = begin;
766 tokenbuf_init(result);
771 /* try to parse substitution text */
772 rc = parse_substext(var, ctx, p, end);
776 if (!tokenbuf_append(result, p, rc)) {
777 rc = VAR_ERR_OUT_OF_MEMORY;
783 /* try to parse substitution text */
784 rc = parse_variable(var, ctx, p, end, &tmp);
789 if (!tokenbuf_merge(result, &tmp)) {
790 rc = VAR_ERR_OUT_OF_MEMORY;
794 tokenbuf_free(&tmp); /* KES 11/9/2003 */
802 tokenbuf_free(result);
806 /* parse class description */
808 parse_class_description(
809 var_t *var, var_parse_t *ctx,
810 tokenbuf_t *src, tokenbuf_t *dst)
816 while (p != src->end) {
817 if ((src->end - p) >= 3 && p[1] == '-') {
819 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
820 for (c = *p, d = p[2]; c <= d; ++c) {
821 if (!tokenbuf_append(dst, (char *)&c, 1))
822 return VAR_ERR_OUT_OF_MEMORY;
826 if (!tokenbuf_append(dst, p, 1))
827 return VAR_ERR_OUT_OF_MEMORY;
834 /* parse regex replace part */
837 var_t *var, var_parse_t *ctx,
841 tokenbuf_t *expanded)
847 tokenbuf_init(expanded);
849 while (p != orig->end) {
851 if (orig->end - p <= 1) {
852 tokenbuf_free(expanded);
853 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
857 if (!tokenbuf_append(expanded, p, 1)) {
858 tokenbuf_free(expanded);
859 return VAR_ERR_OUT_OF_MEMORY;
864 if (!isdigit((int)*p)) {
865 tokenbuf_free(expanded);
866 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
870 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
871 tokenbuf_free(expanded);
872 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
874 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
875 pmatch[i].rm_eo - pmatch[i].rm_so)) {
876 tokenbuf_free(expanded);
877 return VAR_ERR_OUT_OF_MEMORY;
880 if (!tokenbuf_append(expanded, p, 1)) {
881 tokenbuf_free(expanded);
882 return VAR_ERR_OUT_OF_MEMORY;
891 /* operation: transpose */
894 var_t *var, var_parse_t *ctx,
899 tokenbuf_t srcclass, dstclass;
904 tokenbuf_init(&srcclass);
905 tokenbuf_init(&dstclass);
906 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
908 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
910 if (srcclass.begin == srcclass.end) {
911 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
914 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
915 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
918 if (data->buffer_size == 0) {
920 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
921 rc = VAR_ERR_OUT_OF_MEMORY;
924 tokenbuf_move(&tmp, data);
926 for (p = data->begin; p != data->end; ++p) {
927 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
928 if (*p == srcclass.begin[i]) {
929 *((char *)p) = dstclass.begin[i];
934 tokenbuf_free(&srcclass);
935 tokenbuf_free(&dstclass);
939 tokenbuf_free(search);
940 tokenbuf_free(replace);
941 tokenbuf_free(&srcclass);
942 tokenbuf_free(&dstclass);
946 /* operation: search & replace */
948 op_search_and_replace(
949 var_t *var, var_parse_t *ctx,
957 int case_insensitive = 0;
963 if (search->begin == search->end)
964 return VAR_ERR_EMPTY_SEARCH_STRING;
966 for (p = flags->begin; p != flags->end; p++) {
967 switch (tolower(*p)) {
972 case_insensitive = 1;
981 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
986 /* plain text pattern based operation */
988 for (p = data->begin; p != data->end;) {
989 if (case_insensitive)
990 rc = strncasecmp(p, search->begin, search->end - search->begin);
992 rc = strncmp(p, search->begin, search->end - search->begin);
994 /* not matched, copy character */
995 if (!tokenbuf_append(&tmp, p, 1)) {
997 return VAR_ERR_OUT_OF_MEMORY;
1001 /* matched, copy replacement string */
1002 tokenbuf_merge(&tmp, replace);
1003 p += (search->end - search->begin);
1005 /* append remaining text */
1006 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1007 tokenbuf_free(&tmp);
1008 return VAR_ERR_OUT_OF_MEMORY;
1014 tokenbuf_free(data);
1015 tokenbuf_move(&tmp, data);
1017 /* regular expression pattern based operation */
1019 tokenbuf_t myreplace;
1021 regmatch_t pmatch[10];
1024 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1025 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1026 return VAR_ERR_OUT_OF_MEMORY;
1027 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1028 tokenbuf_free(&tmp);
1029 return VAR_ERR_OUT_OF_MEMORY;
1032 /* compile the pattern. */
1033 rc = regcomp(&preg, tmp.begin,
1035 | (multiline ? REG_NEWLINE : 0)
1036 | (case_insensitive ? REG_ICASE : 0)));
1037 tokenbuf_free(&tmp);
1039 tokenbuf_free(&mydata);
1040 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1043 /* match the pattern and create the result string in the tmp buffer */
1044 tokenbuf_append(&tmp, "", 0);
1045 for (p = mydata.begin; p < mydata.end; ) {
1046 if (p == mydata.begin || p[-1] == '\n')
1049 regexec_flag = REG_NOTBOL;
1050 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1052 /* no (more) matching */
1053 tokenbuf_append(&tmp, p, mydata.end - p);
1057 && (p + pmatch[0].rm_so) == mydata.end
1058 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1059 /* special case: found empty pattern (usually /^/ or /$/ only)
1060 in multi-line at end of data (after the last newline) */
1061 tokenbuf_append(&tmp, p, mydata.end - p);
1065 /* append prolog string */
1066 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1068 tokenbuf_free(&tmp);
1069 tokenbuf_free(&mydata);
1070 return VAR_ERR_OUT_OF_MEMORY;
1072 /* create replace string */
1073 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1076 tokenbuf_free(&tmp);
1077 tokenbuf_free(&mydata);
1080 /* append replace string */
1081 if (!tokenbuf_merge(&tmp, &myreplace)) {
1083 tokenbuf_free(&tmp);
1084 tokenbuf_free(&mydata);
1085 tokenbuf_free(&myreplace);
1086 return VAR_ERR_OUT_OF_MEMORY;
1088 tokenbuf_free(&myreplace);
1089 /* skip now processed data */
1090 p += pmatch[0].rm_eo;
1091 /* if pattern matched an empty part (think about
1092 anchor-only regular expressions like /^/ or /$/) we
1093 skip the next character to make sure we do not enter
1094 an infinitive loop in matching */
1095 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1096 if (p >= mydata.end)
1098 if (!tokenbuf_append(&tmp, p, 1)) {
1100 tokenbuf_free(&tmp);
1101 tokenbuf_free(&mydata);
1102 return VAR_ERR_OUT_OF_MEMORY;
1106 /* append prolog string and stop processing if we
1107 do not perform the search & replace globally */
1109 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1111 tokenbuf_free(&tmp);
1112 tokenbuf_free(&mydata);
1113 return VAR_ERR_OUT_OF_MEMORY;
1120 tokenbuf_free(data);
1121 tokenbuf_move(&tmp, data);
1122 tokenbuf_free(&mydata);
1128 /* operation: offset substring */
1131 var_t *var, var_parse_t *ctx,
1140 /* determine begin of result string */
1141 if ((data->end - data->begin) < num1)
1142 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1143 p = data->begin + num1;
1145 /* if num2 is zero, we copy the rest from there. */
1147 if (!tokenbuf_assign(&res, p, data->end - p))
1148 return VAR_ERR_OUT_OF_MEMORY;
1150 /* ok, then use num2. */
1152 if ((p + num2) > data->end)
1153 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1154 if (!tokenbuf_assign(&res, p, num2))
1155 return VAR_ERR_OUT_OF_MEMORY;
1158 return VAR_ERR_OFFSET_LOGIC;
1159 if ((data->begin + num2) > data->end)
1160 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1161 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1162 return VAR_ERR_OUT_OF_MEMORY;
1165 tokenbuf_free(data);
1166 tokenbuf_move(&res, data);
1170 /* operation: padding */
1173 var_t *var, var_parse_t *ctx,
1182 if (fill->begin == fill->end)
1183 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1184 tokenbuf_init(&result);
1185 if (position == 'l') {
1187 i = width - (data->end - data->begin);
1189 i = i / (fill->end - fill->begin);
1191 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1192 return VAR_ERR_OUT_OF_MEMORY;
1195 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1196 if (!tokenbuf_append(data, fill->begin, i))
1197 return VAR_ERR_OUT_OF_MEMORY;
1199 } else if (position == 'r') {
1201 i = width - (data->end - data->begin);
1203 i = i / (fill->end - fill->begin);
1205 if (!tokenbuf_merge(&result, fill)) {
1206 tokenbuf_free(&result);
1207 return VAR_ERR_OUT_OF_MEMORY;
1211 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1212 if (!tokenbuf_append(&result, fill->begin, i)) {
1213 tokenbuf_free(&result);
1214 return VAR_ERR_OUT_OF_MEMORY;
1216 if (!tokenbuf_merge(&result, data)) {
1217 tokenbuf_free(&result);
1218 return VAR_ERR_OUT_OF_MEMORY;
1220 /* move string from temporary buffer to data buffer */
1221 tokenbuf_free(data);
1222 tokenbuf_move(&result, data);
1224 } else if (position == 'c') {
1225 /* centered padding */
1226 i = (width - (data->end - data->begin)) / 2;
1228 /* create the prefix */
1229 i = i / (fill->end - fill->begin);
1231 if (!tokenbuf_merge(&result, fill)) {
1232 tokenbuf_free(&result);
1233 return VAR_ERR_OUT_OF_MEMORY;
1237 i = ((width - (data->end - data->begin)) / 2)
1238 % (fill->end - fill->begin);
1239 if (!tokenbuf_append(&result, fill->begin, i)) {
1240 tokenbuf_free(&result);
1241 return VAR_ERR_OUT_OF_MEMORY;
1243 /* append the actual data string */
1244 if (!tokenbuf_merge(&result, data)) {
1245 tokenbuf_free(&result);
1246 return VAR_ERR_OUT_OF_MEMORY;
1248 /* append the suffix */
1249 i = width - (result.end - result.begin);
1250 i = i / (fill->end - fill->begin);
1252 if (!tokenbuf_merge(&result, fill)) {
1253 tokenbuf_free(&result);
1254 return VAR_ERR_OUT_OF_MEMORY;
1258 i = width - (result.end - result.begin);
1259 if (!tokenbuf_append(&result, fill->begin, i)) {
1260 tokenbuf_free(&result);
1261 return VAR_ERR_OUT_OF_MEMORY;
1263 /* move string from temporary buffer to data buffer */
1264 tokenbuf_free(data);
1265 tokenbuf_move(&result, data);
1271 /* parse an integer number ("123") */
1274 var_t *var, var_parse_t *ctx,
1275 const char *begin, const char *end,
1283 while (isdigit(*p) && p != end) {
1293 /* parse an operation (":x...") */
1296 var_t *var, var_parse_t *ctx,
1297 const char *begin, const char *end,
1301 tokenbuf_t tmptokbuf;
1302 tokenbuf_t search, replace, flags;
1303 tokenbuf_t number1, number2;
1309 /* initialization */
1310 tokenbuf_init(&tmptokbuf);
1311 tokenbuf_init(&search);
1312 tokenbuf_init(&replace);
1313 tokenbuf_init(&flags);
1314 tokenbuf_init(&number1);
1315 tokenbuf_init(&number2);
1320 /* dispatch through the first operation character */
1321 switch (tolower(*p)) {
1323 /* turn value to lowercase. */
1324 if (data->begin != NULL) {
1325 /* if the buffer does not live in an allocated buffer,
1326 we have to copy it before modifying the contents. */
1327 if (data->buffer_size == 0) {
1328 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1329 rc = VAR_ERR_OUT_OF_MEMORY;
1334 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1335 *ptr = (char)tolower((int)(*ptr));
1341 /* turn value to uppercase. */
1342 if (data->begin != NULL) {
1343 /* if the buffer does not live in an allocated buffer,
1344 we have to copy it before modifying the contents. */
1345 if (data->buffer_size == 0) {
1346 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1347 rc = VAR_ERR_OUT_OF_MEMORY;
1352 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1353 *ptr = (char)toupper((int)(*ptr));
1359 /* cut out substring of value. */
1361 rc = parse_integer(var, ctx, p, end, &num1);
1363 rc = VAR_ERR_MISSING_START_OFFSET;
1372 } else if (*p == '-') {
1376 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1379 rc = parse_integer(var, ctx, p, end, &num2);
1381 if (data->begin != NULL) {
1382 rc = op_offset(var, ctx, data, num1, num2, isrange);
1389 /* determine length of the value */
1390 if (data->begin != NULL) {
1391 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1392 sprintf(buf, "%d", (int)(data->end - data->begin));
1393 tokenbuf_free(data);
1394 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1395 rc = VAR_ERR_OUT_OF_MEMORY;
1403 /* substitute parameter if data is empty */
1405 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1409 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1413 if (tokenbuf_isundef(data))
1414 tokenbuf_move(&tmptokbuf, data);
1415 else if (tokenbuf_isempty(data)) {
1416 tokenbuf_free(data);
1417 tokenbuf_move(&tmptokbuf, data);
1422 /* substitute empty string if data is not empty, parameter otherwise. */
1424 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1428 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1432 if (data->begin != NULL) {
1433 if (data->begin == data->end) {
1434 tokenbuf_free(data);
1435 tokenbuf_move(&tmptokbuf, data);
1437 tokenbuf_free(data);
1438 data->begin = data->end = "";
1439 data->buffer_size = 0;
1445 /* substitute parameter if data is not empty. */
1447 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1451 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1455 if (data->begin != NULL && data->begin != data->end) {
1456 tokenbuf_free(data);
1457 tokenbuf_move(&tmptokbuf, data);
1462 /* search and replace. */
1465 return VAR_ERR_MALFORMATTED_REPLACE;
1467 rc = parse_pattern(var, ctx, p, end);
1470 tokenbuf_set(&search, p, p + rc, 0);
1473 rc = VAR_ERR_MALFORMATTED_REPLACE;
1477 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1482 rc = VAR_ERR_MALFORMATTED_REPLACE;
1486 rc = parse_exptext(var, ctx, p, end);
1489 tokenbuf_set(&flags, p, p + rc, 0);
1491 if (data->begin != NULL) {
1492 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1499 /* transpose characters from class A to class B. */
1502 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1504 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1509 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1513 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1518 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1523 rc = op_transpose(var, ctx, data, &search, &replace);
1533 return VAR_ERR_MALFORMATTED_PADDING;
1535 rc = parse_integer(var, ctx, p, end, &num1);
1537 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1542 rc = VAR_ERR_MALFORMATTED_PADDING;
1546 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1551 rc = VAR_ERR_MALFORMATTED_PADDING;
1555 if (*p != 'l' && *p != 'c' && *p != 'r') {
1556 rc = VAR_ERR_MALFORMATTED_PADDING;
1561 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1568 /* operation callback function */
1571 const char *arg_ptr;
1573 const char *val_ptr;
1575 const char *out_ptr;
1581 rc = parse_name(var, ctx, p, end);
1589 tokenbuf_init(&args);
1590 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1594 arg_ptr = args.begin;
1595 arg_len = args.end - args.begin;
1597 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1606 val_ptr = data->begin;
1607 val_len = data->end - data->begin;
1609 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1610 /* call operation callback function */
1611 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1615 &out_ptr, &out_len, &out_size);
1617 if (arg_ptr != NULL)
1618 free((void *)arg_ptr);
1621 tokenbuf_free(data);
1622 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1624 if (arg_ptr != NULL)
1625 free((void *)arg_ptr);
1629 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1632 /* return successfully */
1633 tokenbuf_free(&tmptokbuf);
1634 tokenbuf_free(&search);
1635 tokenbuf_free(&replace);
1636 tokenbuf_free(&flags);
1637 tokenbuf_free(&number1);
1638 tokenbuf_free(&number2);
1641 /* return with an error */
1643 tokenbuf_free(data);
1644 tokenbuf_free(&tmptokbuf);
1645 tokenbuf_free(&search);
1646 tokenbuf_free(&replace);
1647 tokenbuf_free(&flags);
1648 tokenbuf_free(&number1);
1649 tokenbuf_free(&number2);
1653 /* parse numerical expression operand */
1655 parse_numexp_operand(
1656 var_t *var, var_parse_t *ctx,
1657 const char *begin, const char *end,
1658 int *result, int *failed)
1665 /* initialization */
1667 tokenbuf_init(&tmp);
1669 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1671 /* parse opening numerical expression */
1673 /* parse inner numerical expression */
1674 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1679 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1680 /* parse closing parenthesis */
1682 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1685 /* parse contained variable */
1686 else if (*p == var->syntax.delim_init) {
1687 /* parse variable with forced expansion */
1688 ctx = var_parse_push(ctx, &myctx);
1689 ctx->force_expand = 1;
1690 rc = parse_variable(var, ctx, p, end, &tmp);
1691 ctx = var_parse_pop(ctx);
1693 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1695 /* parse variable without forced expansion */
1696 ctx = var_parse_push(ctx, &myctx);
1697 ctx->force_expand = 0;
1698 rc = parse_variable(var, ctx, p, end, &tmp);
1699 ctx = var_parse_pop(ctx);
1704 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1705 } else if (rc < 0) {
1709 /* parse remaining numerical expression */
1710 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1711 tokenbuf_free(&tmp);
1716 /* parse relative index mark ("#") */
1717 else if ( var->syntax.index_mark != EOS
1718 && *p == var->syntax.index_mark) {
1720 *result = ctx->index_this;
1721 if (ctx->rel_lookup_flag)
1722 ctx->rel_lookup_cnt++;
1724 /* parse plain integer number */
1725 else if (isdigit(*p)) {
1726 rc = parse_integer(var, ctx, p, end, result);
1729 /* parse signed positive integer number */
1730 else if (*p == '+') {
1731 if ((end - p) > 1 && isdigit(p[1])) {
1733 rc = parse_integer(var, ctx, p, end, result);
1737 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1739 /* parse signed negative integer number */
1740 else if (*p == '-') {
1741 if (end - p > 1 && isdigit(p[1])) {
1743 rc = parse_integer(var, ctx, p, end, result);
1744 *result = -(*result);
1748 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1750 /* else we failed to parse anything reasonable */
1752 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1757 /* parse numerical expression ("x+y") */
1760 var_t *var, var_parse_t *ctx,
1761 const char *begin, const char *end,
1762 int *result, int *failed)
1769 /* initialization */
1772 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1774 /* parse left numerical operand */
1775 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1780 /* parse numerical operator */
1782 if (*p == '+' || *p == '-') {
1784 /* recursively parse right operand (light binding) */
1785 rc = parse_numexp(var, ctx, p, end, &right, failed);
1790 *result = (*result + right);
1792 *result = (*result - right);
1794 else if (*p == '*' || *p == '/' || *p == '%') {
1796 /* recursively parse right operand (string binding) */
1797 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1802 *result = (*result * right);
1803 else if (op == '/') {
1808 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1811 *result = (*result / right);
1813 else if (op == '%') {
1818 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1821 *result = (*result % right);
1828 /* return amount of parsed input */
1832 /* parse variable name ("abc") */
1835 var_t *var, var_parse_t *ctx,
1836 const char *begin, const char *end)
1840 /* parse as long as name class characters are found */
1841 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1846 /* lookup a variable value through the callback function */
1849 var_t *var, var_parse_t *ctx,
1850 const char *var_ptr, int var_len, int var_inc, int var_idx,
1851 const char **val_ptr, int *val_len, int *val_size)
1856 /* pass through to original callback */
1857 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1858 var_ptr, var_len, var_inc, var_idx,
1859 val_ptr, val_len, val_size);
1861 /* convert undefined variable into empty variable if relative
1862 lookups are counted. This is the case inside an active loop
1863 construct if no limits are given. There the parse_input()
1864 has to proceed until all variables have undefined values.
1865 This trick here allows it to determine this case. */
1866 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1867 ctx->rel_lookup_cnt--;
1878 /* parse complex variable construct ("${name...}") */
1880 parse_variable_complex(
1881 var_t *var, var_parse_t *ctx,
1882 const char *begin, const char *end,
1887 int len, buffer_size;
1895 /* initializations */
1897 tokenbuf_init(&name);
1898 tokenbuf_init(&tmp);
1899 tokenbuf_init(result);
1901 /* parse open delimiter */
1902 if (p == end || *p != var->syntax.delim_open)
1906 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1908 /* parse name of variable to expand. The name may consist of an
1909 arbitrary number of variable name character and contained variable
1912 /* parse a variable name */
1913 rc = parse_name(var, ctx, p, end);
1917 if (!tokenbuf_append(&name, p, rc)) {
1918 rc = VAR_ERR_OUT_OF_MEMORY;
1924 /* parse an (embedded) variable */
1925 rc = parse_variable(var, ctx, p, end, &tmp);
1929 if (!tokenbuf_merge(&name, &tmp)) {
1930 rc = VAR_ERR_OUT_OF_MEMORY;
1935 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1938 /* we must have the complete expanded variable name now,
1939 so make sure we really do. */
1940 if (name.begin == name.end) {
1941 if (ctx->force_expand) {
1942 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1946 /* If no force_expand is requested, we have to back-off.
1947 We're not sure whether our approach here is 100% correct,
1948 because it _could_ have side-effects according to Peter
1949 Simons, but as far as we know and tried it, it is
1950 correct. But be warned -- RSE */
1951 tokenbuf_set(result, begin - 1, p, 0);
1956 /* parse an optional index specification */
1957 if ( var->syntax.index_open != EOS
1958 && *p == var->syntax.index_open) {
1960 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1964 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1969 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1972 if (*p != var->syntax.index_close) {
1973 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1979 /* parse end of variable construct or start of post-operations */
1980 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1981 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1984 inc = (*p == '+'); /* increment variable */
1987 /* lookup the variable value now */
1989 tokenbuf_set(result, begin - 1, p, 0);
1991 rc = lookup_value(var, ctx,
1992 name.begin, name.end-name.begin, inc, idx,
1993 &data, &len, &buffer_size);
1994 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1995 tokenbuf_init(result); /* delayed handling of undefined variable */
1996 } else if (rc < 0) {
1999 /* the preliminary result is the raw value of the variable.
2000 This may be modified by the operations that may follow. */
2001 tokenbuf_set(result, data, data + len, buffer_size);
2005 /* parse optional post-operations */
2008 tokenbuf_free(&tmp);
2009 tokenbuf_init(&tmp);
2011 while (p != end && *p == ':') {
2014 rc = parse_operation(var, ctx, p, end, result);
2016 rc = parse_operation(var, ctx, p, end, &tmp);
2023 if (p == end || *p != var->syntax.delim_close) {
2024 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2030 } else if (p[-1] == '+') {
2034 /* lazy handling of undefined variable */
2035 if (!failed && tokenbuf_isundef(result)) {
2036 if (ctx->force_expand) {
2037 rc = VAR_ERR_UNDEFINED_VARIABLE;
2040 tokenbuf_set(result, begin - 1, p, 0);
2044 /* return successfully */
2045 tokenbuf_free(&name);
2046 tokenbuf_free(&tmp);
2049 /* return with an error */
2051 tokenbuf_free(&name);
2052 tokenbuf_free(&tmp);
2053 tokenbuf_free(result);
2057 /* parse variable construct ("$name" or "${name...}") */
2060 var_t *var, var_parse_t *ctx,
2061 const char *begin, const char *end,
2066 int len, buffer_size;
2070 /* initialization */
2072 tokenbuf_init(result);
2074 /* parse init delimiter */
2075 if (p == end || *p != var->syntax.delim_init)
2079 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2081 /* parse a simple variable name.
2082 (if this fails, we're try to parse a complex variable construct) */
2083 rc = parse_name(var, ctx, p, end);
2087 inc = (p[rc] == '+');
2088 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2089 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2090 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2095 tokenbuf_set(result, data, data + len, buffer_size);
2099 /* parse a complex variable construct (else case) */
2100 rc = parse_variable_complex(var, ctx, p, end, result);
2106 /* parse loop construct limits ("[...]{b,s,e}") */
2109 var_t *var, var_parse_t *ctx,
2110 const char *begin, const char *end,
2111 int *start, int *step, int *stop, int *open_stop)
2117 /* initialization */
2120 /* we are happy if nothing is to left to parse */
2124 /* parse start delimiter */
2125 if (*p != var->syntax.delim_open)
2129 /* parse loop start value */
2131 rc = parse_numexp(var, ctx, p, end, start, &failed);
2132 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2133 *start = 0; /* use default */
2135 return (var_rc_t)rc;
2139 return VAR_ERR_UNDEFINED_VARIABLE;
2141 /* parse separator */
2143 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2146 /* parse loop step value */
2148 rc = parse_numexp(var, ctx, p, end, step, &failed);
2149 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2150 *step = 1; /* use default */
2152 return (var_rc_t)rc;
2156 return VAR_ERR_UNDEFINED_VARIABLE;
2158 /* parse separator */
2160 /* if not found, parse end delimiter */
2161 if (*p != var->syntax.delim_close)
2162 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2165 /* shift step value to stop value */
2169 /* determine whether loop end is open */
2174 return (var_rc_t)(p - begin);
2178 /* parse loop stop value */
2180 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2181 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2182 *stop = 0; /* use default */
2186 return (var_rc_t)rc;
2192 return VAR_ERR_UNDEFINED_VARIABLE;
2194 /* parse end delimiter */
2195 if (*p != var->syntax.delim_close)
2196 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2199 /* return amount of parsed input */
2200 return (var_rc_t)(p - begin);
2203 /* parse plain text */
2206 var_t *var, var_parse_t *ctx,
2207 const char *begin, const char *end)
2211 /* parse until delim_init (variable construct)
2212 or index_open (loop construct) is found */
2213 for (p = begin; p != end; p++) {
2214 if (*p == var->syntax.escape) {
2215 p++; /* skip next character */
2217 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2219 else if (*p == var->syntax.delim_init)
2221 else if ( var->syntax.index_open != EOS
2222 && ( *p == var->syntax.index_open
2223 || *p == var->syntax.index_close))
2229 /* expand input in general */
2232 var_t *var, var_parse_t *ctx,
2233 const char *begin, const char *end,
2234 tokenbuf_t *output, int recursion_level)
2239 int start, step, stop, open_stop;
2243 int loop_limit_length;
2246 /* initialization */
2250 /* try to parse a loop construct */
2252 && var->syntax.index_open != EOS
2253 && *p == var->syntax.index_open) {
2256 /* loop preparation */
2257 loop_limit_length = -1;
2258 rel_lookup_cnt = ctx->rel_lookup_cnt;
2266 /* iterate over loop construct, either as long as there is
2267 (still) nothing known about the limit, or there is an open
2268 (=unknown) limit stop and there are still defined variables
2269 or there is a stop limit known and it is still not reached */
2273 && ( loop_limit_length < 0
2274 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2279 /* remember current output end for restoring */
2280 output_backup = (output->end - output->begin);
2282 /* open temporary context for recursion */
2283 ctx = var_parse_push(ctx, &myctx);
2284 ctx->force_expand = 1;
2285 ctx->rel_lookup_flag = 1;
2286 ctx->index_this = i;
2288 /* recursive parse input through ourself */
2289 rc = parse_input(var, ctx, p, end,
2290 output, recursion_level+1);
2292 /* retrieve info and close temporary context */
2293 rel_lookup_cnt = ctx->rel_lookup_cnt;
2294 ctx = var_parse_pop(ctx);
2296 /* error handling */
2300 /* make sure the loop construct is closed */
2301 if (p[rc] != var->syntax.index_close) {
2302 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2306 /* try to parse loop construct limit specification */
2307 if (loop_limit_length < 0) {
2308 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2309 &start, &step, &stop, &open_stop);
2313 loop_limit_length = 0;
2315 loop_limit_length = rc2;
2316 /* restart loop from scratch */
2317 output->end = (output->begin + output_backup);
2323 /* if stop value is open, restore to the output end
2324 because the last iteration was just to determine the loop
2325 termination and its result has to be discarded */
2327 output->end = (output->begin + output_backup);
2329 /* skip parsed loop construct */
2332 p += loop_limit_length;
2337 /* try to parse plain text */
2338 rc = parse_text(var, ctx, p, end);
2340 if (!tokenbuf_append(output, p, rc)) {
2341 rc = VAR_ERR_OUT_OF_MEMORY;
2349 /* try to parse a variable construct */
2350 tokenbuf_init(&result);
2351 rc = parse_variable(var, ctx, p, end, &result);
2353 if (!tokenbuf_merge(output, &result)) {
2354 tokenbuf_free(&result);
2355 rc = VAR_ERR_OUT_OF_MEMORY;
2358 tokenbuf_free(&result);
2362 tokenbuf_free(&result);
2366 } while (p != end && rc > 0);
2368 /* We do not know whether this really could happen, but because we
2369 are paranoid, report an error at the outer most parsing level if
2370 there is still any input. Because this would mean that we are no
2371 longer able to parse the remaining input as a loop construct, a
2372 text or a variable construct. This would be very strange, but
2373 could perhaps happen in case of configuration errors!?... */
2374 if (recursion_level == 0 && p != end) {
2375 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2379 /* return amount of parsed text */
2380 return (var_rc_t)(p - begin);
2382 /* return with an error where as a special case the output begin is
2383 set to the input begin and the output end to the last input parsing
2386 tokenbuf_free(output);
2387 tokenbuf_set(output, begin, p, 0);
2388 return (var_rc_t)rc;
2393 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2397 /* create variable expansion context */
2405 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2406 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2407 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2408 memset(var, 0, sizeof(var));
2409 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2414 /* destroy variable expansion context */
2420 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2425 /* configure variable expansion context */
2436 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2439 case VAR_CONFIG_SYNTAX: {
2441 s = (var_syntax_t *)va_arg(ap, void *);
2443 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2444 var->syntax.escape = s->escape;
2445 var->syntax.delim_init = s->delim_init;
2446 var->syntax.delim_open = s->delim_open;
2447 var->syntax.delim_close = s->delim_close;
2448 var->syntax.index_open = s->index_open;
2449 var->syntax.index_close = s->index_close;
2450 var->syntax.index_mark = s->index_mark;
2451 var->syntax.name_chars = NULL; /* unused internally */
2452 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2454 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2455 || var->syntax_nameclass[(int)var->syntax.delim_open]
2456 || var->syntax_nameclass[(int)var->syntax.delim_close]
2457 || var->syntax_nameclass[(int)var->syntax.escape])
2458 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2461 case VAR_CONFIG_CB_VALUE: {
2464 fct = (var_cb_value_t)va_arg(ap, void *);
2465 ctx = (void *)va_arg(ap, void *);
2466 var->cb_value_fct = fct;
2467 var->cb_value_ctx = ctx;
2470 case VAR_CONFIG_CB_OPERATION: {
2471 var_cb_operation_t fct;
2473 fct = (var_cb_operation_t)va_arg(ap, void *);
2474 ctx = (void *)va_arg(ap, void *);
2475 var->cb_operation_fct = fct;
2476 var->cb_operation_ctx = ctx;
2480 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2486 /* perform unescape operation on a buffer */
2490 const char *src, int srclen,
2491 char *dst, int dstlen,
2497 if (var == NULL || src == NULL || dst == NULL)
2498 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2503 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2522 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2525 case '0': case '1': case '2': case '3': case '4':
2526 case '5': case '6': case '7': case '8': case '9':
2528 && isdigit((int)src[1])
2529 && isdigit((int)src[2])) {
2530 if ((rc = expand_octal(&src, &dst, end)) != 0)
2548 /* perform expand operation on a buffer */
2552 const char *src_ptr, int src_len,
2553 char **dst_ptr, int *dst_len,
2560 /* argument sanity checks */
2561 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2562 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2564 /* prepare internal expansion context */
2566 ctx.force_expand = force_expand;
2567 ctx.rel_lookup_flag = 0;
2568 ctx.rel_lookup_cnt = 0;
2571 /* start the parsing */
2572 tokenbuf_init(&output);
2573 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2575 /* post-processing */
2577 /* always EOS-terminate output for convinience reasons
2578 but do not count the EOS-terminator in the length */
2579 if (!tokenbuf_append(&output, "\0", 1)) {
2580 tokenbuf_free(&output);
2581 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2585 /* provide result */
2586 *dst_ptr = (char *)output.begin;
2587 if (dst_len != NULL)
2588 *dst_len = (output.end - output.begin);
2592 /* provide result */
2593 if (dst_len != NULL)
2594 *dst_len = (output.end - output.begin);
2600 /* format and expand a string */
2604 char **dst_ptr, int force_expand,
2605 const char *fmt, va_list ap)
2611 /* argument sanity checks */
2612 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2613 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2615 /* perform formatting */
2616 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2617 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2618 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2621 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2624 /* perform expansion */
2625 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2636 /* format and expand a string */
2640 char **dst_ptr, int force_expand,
2641 const char *fmt, ...)
2646 /* argument sanity checks */
2647 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2648 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2651 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2657 /* var_rc_t to string mapping table */
2658 static const char *var_errors[] = {
2659 "everything ok", /* VAR_OK = 0 */
2660 "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2661 "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */
2662 "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */
2663 "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */
2664 "invalid octal value", /* VAR_ERR_INVALID_OCTAL */
2665 "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */
2666 "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2667 "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */
2668 "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */
2669 "out of memory", /* VAR_ERR_OUT_OF_MEMORY */
2670 "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2671 "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */
2672 "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2673 "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2674 "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */
2675 "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2676 "invalid regex in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2677 "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2678 "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */
2679 "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */
2680 "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2681 "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2682 "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2683 "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */
2684 "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2685 "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2686 "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2687 "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2688 "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */
2689 "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */
2690 "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2691 "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2692 "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2693 "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */
2694 "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2695 "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2696 "index of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2697 "index of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2698 "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2699 "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2700 "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2701 "invalid character in loop limits", /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2702 "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2703 "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */
2704 "formatting failure" /* VAR_ERR_FORMATTING_FAILURE */
2707 /* translate a return code into its corresponding descriptive text */
2708 char *var_strerror(var_t *var, var_rc_t rc)
2711 rc = (var_rc_t)(0 - rc);
2712 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2713 str = "unknown error";
2715 str = (char *)var_errors[rc];