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;
699 tokenbuf_free(result);
703 /* parse expression or variable */
705 parse_exptext_or_variable(
706 var_t *var, var_parse_t *ctx,
707 const char *begin, const char *end,
710 const char *p = begin;
714 tokenbuf_init(result);
719 /* try to parse expression text */
720 rc = parse_exptext(var, ctx, p, end);
724 if (!tokenbuf_append(result, p, rc)) {
725 rc = VAR_ERR_OUT_OF_MEMORY;
731 /* try to parse variable construct */
732 rc = parse_variable(var, ctx, p, end, &tmp);
738 (result, tmp.begin, tmp.end - tmp.begin)) {
739 rc = VAR_ERR_OUT_OF_MEMORY;
750 tokenbuf_free(result);
754 /* parse substitution text or variable */
756 parse_substext_or_variable(
757 var_t *var, var_parse_t *ctx,
758 const char *begin, const char *end,
761 const char *p = begin;
765 tokenbuf_init(result);
770 /* try to parse substitution text */
771 rc = parse_substext(var, ctx, p, end);
775 if (!tokenbuf_append(result, p, rc)) {
776 rc = VAR_ERR_OUT_OF_MEMORY;
782 /* try to parse substitution text */
783 rc = parse_variable(var, ctx, p, end, &tmp);
789 (result, tmp.begin, tmp.end - tmp.begin)) {
790 rc = VAR_ERR_OUT_OF_MEMORY;
801 tokenbuf_free(result);
805 /* parse class description */
807 parse_class_description(
808 var_t *var, var_parse_t *ctx,
809 tokenbuf_t *src, tokenbuf_t *dst)
815 while (p != src->end) {
816 if ((src->end - p) >= 3 && p[1] == '-') {
818 return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
819 for (c = *p, d = p[2]; c <= d; ++c) {
820 if (!tokenbuf_append(dst, (char *)&c, 1))
821 return VAR_ERR_OUT_OF_MEMORY;
825 if (!tokenbuf_append(dst, p, 1))
826 return VAR_ERR_OUT_OF_MEMORY;
833 /* parse regex replace part */
836 var_t *var, var_parse_t *ctx,
840 tokenbuf_t *expanded)
846 tokenbuf_init(expanded);
848 while (p != orig->end) {
850 if (orig->end - p <= 1) {
851 tokenbuf_free(expanded);
852 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
856 if (!tokenbuf_append(expanded, p, 1)) {
857 tokenbuf_free(expanded);
858 return VAR_ERR_OUT_OF_MEMORY;
863 if (!isdigit((int)*p)) {
864 tokenbuf_free(expanded);
865 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
869 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
870 tokenbuf_free(expanded);
871 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
873 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
874 pmatch[i].rm_eo - pmatch[i].rm_so)) {
875 tokenbuf_free(expanded);
876 return VAR_ERR_OUT_OF_MEMORY;
879 if (!tokenbuf_append(expanded, p, 1)) {
880 tokenbuf_free(expanded);
881 return VAR_ERR_OUT_OF_MEMORY;
890 /* operation: transpose */
893 var_t *var, var_parse_t *ctx,
898 tokenbuf_t srcclass, dstclass;
903 tokenbuf_init(&srcclass);
904 tokenbuf_init(&dstclass);
905 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
907 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
909 if (srcclass.begin == srcclass.end) {
910 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
913 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
914 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
917 if (data->buffer_size == 0) {
919 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
920 rc = VAR_ERR_OUT_OF_MEMORY;
923 tokenbuf_move(&tmp, data);
925 for (p = data->begin; p != data->end; ++p) {
926 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
927 if (*p == srcclass.begin[i]) {
928 *((char *)p) = dstclass.begin[i];
933 tokenbuf_free(&srcclass);
934 tokenbuf_free(&dstclass);
938 tokenbuf_free(search);
939 tokenbuf_free(replace);
940 tokenbuf_free(&srcclass);
941 tokenbuf_free(&dstclass);
945 /* operation: search & replace */
947 op_search_and_replace(
948 var_t *var, var_parse_t *ctx,
956 int case_insensitive = 0;
962 if (search->begin == search->end)
963 return VAR_ERR_EMPTY_SEARCH_STRING;
965 for (p = flags->begin; p != flags->end; p++) {
966 switch (tolower(*p)) {
971 case_insensitive = 1;
980 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
985 /* plain text pattern based operation */
987 for (p = data->begin; p != data->end;) {
988 if (case_insensitive)
989 rc = strncasecmp(p, search->begin, search->end - search->begin);
991 rc = strncmp(p, search->begin, search->end - search->begin);
993 /* not matched, copy character */
994 if (!tokenbuf_append(&tmp, p, 1)) {
996 return VAR_ERR_OUT_OF_MEMORY;
1000 /* matched, copy replacement string */
1001 tokenbuf_merge(&tmp, replace);
1002 p += (search->end - search->begin);
1004 /* append remaining text */
1005 if (!tokenbuf_append(&tmp, p, data->end - p)) {
1006 tokenbuf_free(&tmp);
1007 return VAR_ERR_OUT_OF_MEMORY;
1013 tokenbuf_free(data);
1014 tokenbuf_move(&tmp, data);
1016 /* regular expression pattern based operation */
1018 tokenbuf_t myreplace;
1020 regmatch_t pmatch[10];
1023 /* copy pattern and data to own buffer to make sure they are EOS-terminated */
1024 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
1025 return VAR_ERR_OUT_OF_MEMORY;
1026 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
1027 tokenbuf_free(&tmp);
1028 return VAR_ERR_OUT_OF_MEMORY;
1031 /* compile the pattern. */
1032 rc = regcomp(&preg, tmp.begin,
1034 | (multiline ? REG_NEWLINE : 0)
1035 | (case_insensitive ? REG_ICASE : 0)));
1036 tokenbuf_free(&tmp);
1038 tokenbuf_free(&mydata);
1039 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
1042 /* match the pattern and create the result string in the tmp buffer */
1043 tokenbuf_append(&tmp, "", 0);
1044 for (p = mydata.begin; p < mydata.end; ) {
1045 if (p == mydata.begin || p[-1] == '\n')
1048 regexec_flag = REG_NOTBOL;
1049 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
1051 /* no (more) matching */
1052 tokenbuf_append(&tmp, p, mydata.end - p);
1056 && (p + pmatch[0].rm_so) == mydata.end
1057 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1058 /* special case: found empty pattern (usually /^/ or /$/ only)
1059 in multi-line at end of data (after the last newline) */
1060 tokenbuf_append(&tmp, p, mydata.end - p);
1064 /* append prolog string */
1065 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
1067 tokenbuf_free(&tmp);
1068 tokenbuf_free(&mydata);
1069 return VAR_ERR_OUT_OF_MEMORY;
1071 /* create replace string */
1072 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
1075 tokenbuf_free(&tmp);
1076 tokenbuf_free(&mydata);
1079 /* append replace string */
1080 if (!tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) {
1082 tokenbuf_free(&tmp);
1083 tokenbuf_free(&mydata);
1084 tokenbuf_free(&myreplace);
1085 return VAR_ERR_OUT_OF_MEMORY;
1087 tokenbuf_free(&myreplace);
1088 /* skip now processed data */
1089 p += pmatch[0].rm_eo;
1090 /* if pattern matched an empty part (think about
1091 anchor-only regular expressions like /^/ or /$/) we
1092 skip the next character to make sure we do not enter
1093 an infinitive loop in matching */
1094 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1095 if (p >= mydata.end)
1097 if (!tokenbuf_append(&tmp, p, 1)) {
1099 tokenbuf_free(&tmp);
1100 tokenbuf_free(&mydata);
1101 return VAR_ERR_OUT_OF_MEMORY;
1105 /* append prolog string and stop processing if we
1106 do not perform the search & replace globally */
1108 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1110 tokenbuf_free(&tmp);
1111 tokenbuf_free(&mydata);
1112 return VAR_ERR_OUT_OF_MEMORY;
1119 tokenbuf_free(data);
1120 tokenbuf_move(&tmp, data);
1121 tokenbuf_free(&mydata);
1127 /* operation: offset substring */
1130 var_t *var, var_parse_t *ctx,
1139 /* determine begin of result string */
1140 if ((data->end - data->begin) < num1)
1141 return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1142 p = data->begin + num1;
1144 /* if num2 is zero, we copy the rest from there. */
1146 if (!tokenbuf_assign(&res, p, data->end - p))
1147 return VAR_ERR_OUT_OF_MEMORY;
1149 /* ok, then use num2. */
1151 if ((p + num2) > data->end)
1152 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1153 if (!tokenbuf_assign(&res, p, num2))
1154 return VAR_ERR_OUT_OF_MEMORY;
1157 return VAR_ERR_OFFSET_LOGIC;
1158 if ((data->begin + num2) > data->end)
1159 return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1160 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1161 return VAR_ERR_OUT_OF_MEMORY;
1164 tokenbuf_free(data);
1165 tokenbuf_move(&res, data);
1169 /* operation: padding */
1172 var_t *var, var_parse_t *ctx,
1181 if (fill->begin == fill->end)
1182 return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1183 tokenbuf_init(&result);
1184 if (position == 'l') {
1186 i = width - (data->end - data->begin);
1188 i = i / (fill->end - fill->begin);
1190 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1191 return VAR_ERR_OUT_OF_MEMORY;
1194 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1195 if (!tokenbuf_append(data, fill->begin, i))
1196 return VAR_ERR_OUT_OF_MEMORY;
1198 } else if (position == 'r') {
1200 i = width - (data->end - data->begin);
1202 i = i / (fill->end - fill->begin);
1204 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
1205 tokenbuf_free(&result);
1206 return VAR_ERR_OUT_OF_MEMORY;
1210 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1211 if (!tokenbuf_append(&result, fill->begin, i)) {
1212 tokenbuf_free(&result);
1213 return VAR_ERR_OUT_OF_MEMORY;
1215 if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) {
1216 tokenbuf_free(&result);
1217 return VAR_ERR_OUT_OF_MEMORY;
1219 /* move string from temporary buffer to data buffer */
1220 tokenbuf_free(data);
1221 tokenbuf_move(&result, data);
1223 } else if (position == 'c') {
1224 /* centered padding */
1225 i = (width - (data->end - data->begin)) / 2;
1227 /* create the prefix */
1228 i = i / (fill->end - fill->begin);
1230 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
1231 tokenbuf_free(&result);
1232 return VAR_ERR_OUT_OF_MEMORY;
1236 i = ((width - (data->end - data->begin)) / 2)
1237 % (fill->end - fill->begin);
1238 if (!tokenbuf_append(&result, fill->begin, i)) {
1239 tokenbuf_free(&result);
1240 return VAR_ERR_OUT_OF_MEMORY;
1242 /* append the actual data string */
1243 if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) {
1244 tokenbuf_free(&result);
1245 return VAR_ERR_OUT_OF_MEMORY;
1247 /* append the suffix */
1248 i = width - (result.end - result.begin);
1249 i = i / (fill->end - fill->begin);
1251 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
1252 tokenbuf_free(&result);
1253 return VAR_ERR_OUT_OF_MEMORY;
1257 i = width - (result.end - result.begin);
1258 if (!tokenbuf_append(&result, fill->begin, i)) {
1259 tokenbuf_free(&result);
1260 return VAR_ERR_OUT_OF_MEMORY;
1262 /* move string from temporary buffer to data buffer */
1263 tokenbuf_free(data);
1264 tokenbuf_move(&result, data);
1270 /* parse an integer number ("123") */
1273 var_t *var, var_parse_t *ctx,
1274 const char *begin, const char *end,
1282 while (isdigit(*p) && p != end) {
1292 /* parse an operation (":x...") */
1295 var_t *var, var_parse_t *ctx,
1296 const char *begin, const char *end,
1300 tokenbuf_t tmptokbuf;
1301 tokenbuf_t search, replace, flags;
1302 tokenbuf_t number1, number2;
1308 /* initialization */
1309 tokenbuf_init(&tmptokbuf);
1310 tokenbuf_init(&search);
1311 tokenbuf_init(&replace);
1312 tokenbuf_init(&flags);
1313 tokenbuf_init(&number1);
1314 tokenbuf_init(&number2);
1319 /* dispatch through the first operation character */
1320 switch (tolower(*p)) {
1322 /* turn value to lowercase. */
1323 if (data->begin != NULL) {
1324 /* if the buffer does not live in an allocated buffer,
1325 we have to copy it before modifying the contents. */
1326 if (data->buffer_size == 0) {
1327 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1328 rc = VAR_ERR_OUT_OF_MEMORY;
1333 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1334 *ptr = (char)tolower((int)(*ptr));
1340 /* turn value to uppercase. */
1341 if (data->begin != NULL) {
1342 /* if the buffer does not live in an allocated buffer,
1343 we have to copy it before modifying the contents. */
1344 if (data->buffer_size == 0) {
1345 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1346 rc = VAR_ERR_OUT_OF_MEMORY;
1351 for (ptr = (char *)data->begin; ptr != data->end; ptr++)
1352 *ptr = (char)toupper((int)(*ptr));
1358 /* cut out substring of value. */
1360 rc = parse_integer(var, ctx, p, end, &num1);
1362 rc = VAR_ERR_MISSING_START_OFFSET;
1371 } else if (*p == '-') {
1375 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1378 rc = parse_integer(var, ctx, p, end, &num2);
1380 if (data->begin != NULL) {
1381 rc = op_offset(var, ctx, data, num1, num2, isrange);
1388 /* determine length of the value */
1389 if (data->begin != NULL) {
1390 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
1391 sprintf(buf, "%d", (int)(data->end - data->begin));
1392 tokenbuf_free(data);
1393 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1394 rc = VAR_ERR_OUT_OF_MEMORY;
1402 /* substitute parameter if data is empty */
1404 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1408 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1412 if (tokenbuf_isundef(data))
1413 tokenbuf_move(&tmptokbuf, data);
1414 else if (tokenbuf_isempty(data)) {
1415 tokenbuf_free(data);
1416 tokenbuf_move(&tmptokbuf, data);
1421 /* substitute empty string if data is not empty, parameter otherwise. */
1423 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1427 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1431 if (data->begin != NULL) {
1432 if (data->begin == data->end) {
1433 tokenbuf_free(data);
1434 tokenbuf_move(&tmptokbuf, data);
1436 tokenbuf_free(data);
1437 data->begin = data->end = "";
1438 data->buffer_size = 0;
1444 /* substitute parameter if data is not empty. */
1446 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1450 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1454 if (data->begin != NULL && data->begin != data->end) {
1455 tokenbuf_free(data);
1456 tokenbuf_move(&tmptokbuf, data);
1461 /* search and replace. */
1464 return VAR_ERR_MALFORMATTED_REPLACE;
1466 rc = parse_pattern(var, ctx, p, end);
1469 tokenbuf_set(&search, p, p + rc, 0);
1472 rc = VAR_ERR_MALFORMATTED_REPLACE;
1476 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1481 rc = VAR_ERR_MALFORMATTED_REPLACE;
1485 rc = parse_exptext(var, ctx, p, end);
1488 tokenbuf_set(&flags, p, p + rc, 0);
1490 if (data->begin != NULL) {
1491 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1498 /* transpose characters from class A to class B. */
1501 return VAR_ERR_MALFORMATTED_TRANSPOSE;
1503 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1508 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1512 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1517 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1522 rc = op_transpose(var, ctx, data, &search, &replace);
1532 return VAR_ERR_MALFORMATTED_PADDING;
1534 rc = parse_integer(var, ctx, p, end, &num1);
1536 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1541 rc = VAR_ERR_MALFORMATTED_PADDING;
1545 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1550 rc = VAR_ERR_MALFORMATTED_PADDING;
1554 if (*p != 'l' && *p != 'c' && *p != 'r') {
1555 rc = VAR_ERR_MALFORMATTED_PADDING;
1560 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1567 /* operation callback function */
1570 const char *arg_ptr;
1572 const char *val_ptr;
1574 const char *out_ptr;
1580 rc = parse_name(var, ctx, p, end);
1588 tokenbuf_init(&args);
1589 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1593 arg_ptr = args.begin;
1594 arg_len = args.end - args.begin;
1596 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1605 val_ptr = data->begin;
1606 val_len = data->end - data->begin;
1608 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1609 /* call operation callback function */
1610 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
1614 &out_ptr, &out_len, &out_size);
1617 tokenbuf_free(data);
1618 tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
1623 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1626 /* return successfully */
1627 tokenbuf_free(&tmptokbuf);
1628 tokenbuf_free(&search);
1629 tokenbuf_free(&replace);
1630 tokenbuf_free(&flags);
1631 tokenbuf_free(&number1);
1632 tokenbuf_free(&number2);
1635 /* return with an error */
1637 tokenbuf_free(data);
1638 tokenbuf_free(&tmptokbuf);
1639 tokenbuf_free(&search);
1640 tokenbuf_free(&replace);
1641 tokenbuf_free(&flags);
1642 tokenbuf_free(&number1);
1643 tokenbuf_free(&number2);
1647 /* parse numerical expression operand */
1649 parse_numexp_operand(
1650 var_t *var, var_parse_t *ctx,
1651 const char *begin, const char *end,
1652 int *result, int *failed)
1659 /* initialization */
1661 tokenbuf_init(&tmp);
1663 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1665 /* parse opening numerical expression */
1667 /* parse inner numerical expression */
1668 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1673 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1674 /* parse closing parenthesis */
1676 return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1679 /* parse contained variable */
1680 else if (*p == var->syntax.delim_init) {
1681 /* parse variable with forced expansion */
1682 ctx = var_parse_push(ctx, &myctx);
1683 ctx->force_expand = 1;
1684 rc = parse_variable(var, ctx, p, end, &tmp);
1685 ctx = var_parse_pop(ctx);
1687 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1689 /* parse variable without forced expansion */
1690 ctx = var_parse_push(ctx, &myctx);
1691 ctx->force_expand = 0;
1692 rc = parse_variable(var, ctx, p, end, &tmp);
1693 ctx = var_parse_pop(ctx);
1703 /* parse remaining numerical expression */
1704 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1705 tokenbuf_free(&tmp);
1710 /* parse relative index mark ("#") */
1711 else if ( var->syntax.index_mark != EOS
1712 && *p == var->syntax.index_mark) {
1714 *result = ctx->index_this;
1715 if (ctx->rel_lookup_flag)
1716 ctx->rel_lookup_cnt++;
1718 /* parse plain integer number */
1719 else if (isdigit(*p)) {
1720 rc = parse_integer(var, ctx, p, end, result);
1723 /* parse signed positive integer number */
1724 else if (*p == '+') {
1725 if ((end - p) > 1 && isdigit(p[1])) {
1727 rc = parse_integer(var, ctx, p, end, result);
1731 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1733 /* parse signed negative integer number */
1734 else if (*p == '-') {
1735 if (end - p > 1 && isdigit(p[1])) {
1737 rc = parse_integer(var, ctx, p, end, result);
1738 *result = -(*result);
1742 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1744 /* else we failed to parse anything reasonable */
1746 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1751 /* parse numerical expression ("x+y") */
1754 var_t *var, var_parse_t *ctx,
1755 const char *begin, const char *end,
1756 int *result, int *failed)
1763 /* initialization */
1766 return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1768 /* parse left numerical operand */
1769 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1774 /* parse numerical operator */
1776 if (*p == '+' || *p == '-') {
1778 /* recursively parse right operand (light binding) */
1779 rc = parse_numexp(var, ctx, p, end, &right, failed);
1784 *result = (*result + right);
1786 *result = (*result - right);
1788 else if (*p == '*' || *p == '/' || *p == '%') {
1790 /* recursively parse right operand (string binding) */
1791 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1796 *result = (*result * right);
1797 else if (op == '/') {
1802 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1805 *result = (*result / right);
1807 else if (op == '%') {
1812 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1815 *result = (*result % right);
1822 /* return amount of parsed input */
1826 /* parse variable name ("abc") */
1829 var_t *var, var_parse_t *ctx,
1830 const char *begin, const char *end)
1834 /* parse as long as name class characters are found */
1835 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1840 /* lookup a variable value through the callback function */
1843 var_t *var, var_parse_t *ctx,
1844 const char *var_ptr, int var_len, int var_inc, int var_idx,
1845 const char **val_ptr, int *val_len, int *val_size)
1850 /* pass through to original callback */
1851 rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
1852 var_ptr, var_len, var_inc, var_idx,
1853 val_ptr, val_len, val_size);
1855 /* convert undefined variable into empty variable if relative
1856 lookups are counted. This is the case inside an active loop
1857 construct if no limits are given. There the parse_input()
1858 has to proceed until all variables have undefined values.
1859 This trick here allows it to determine this case. */
1860 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1861 ctx->rel_lookup_cnt--;
1872 /* parse complex variable construct ("${name...}") */
1874 parse_variable_complex(
1875 var_t *var, var_parse_t *ctx,
1876 const char *begin, const char *end,
1881 int len, buffer_size;
1889 /* initializations */
1891 tokenbuf_init(&name);
1892 tokenbuf_init(&tmp);
1893 tokenbuf_init(result);
1895 /* parse open delimiter */
1896 if (p == end || *p != var->syntax.delim_open)
1900 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1902 /* parse name of variable to expand. The name may consist of an
1903 arbitrary number of variable name character and contained variable
1906 /* parse a variable name */
1907 rc = parse_name(var, ctx, p, end);
1911 if (!tokenbuf_append(&name, p, rc)) {
1912 rc = VAR_ERR_OUT_OF_MEMORY;
1918 /* parse an (embedded) variable */
1919 rc = parse_variable(var, ctx, p, end, &tmp);
1923 if (!tokenbuf_merge(&name, &tmp)) {
1924 rc = VAR_ERR_OUT_OF_MEMORY;
1931 /* we must have the complete expanded variable name now,
1932 so make sure we really do. */
1933 if (name.begin == name.end) {
1934 if (ctx->force_expand) {
1935 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1939 /* If no force_expand is requested, we have to back-off.
1940 We're not sure whether our approach here is 100% correct,
1941 because it _could_ have side-effects according to Peter
1942 Simons, but as far as we know and tried it, it is
1943 correct. But be warned -- RSE */
1944 tokenbuf_set(result, begin - 1, p, 0);
1949 /* parse an optional index specification */
1950 if ( var->syntax.index_open != EOS
1951 && *p == var->syntax.index_open) {
1953 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1957 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1962 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1965 if (*p != var->syntax.index_close) {
1966 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1972 /* parse end of variable construct or start of post-operations */
1973 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1974 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1977 inc = (*p == '+'); /* increment variable */
1980 /* lookup the variable value now */
1982 tokenbuf_set(result, begin - 1, p, 0);
1984 rc = lookup_value(var, ctx,
1985 name.begin, name.end-name.begin, inc, idx,
1986 &data, &len, &buffer_size);
1987 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1988 tokenbuf_init(result); /* delayed handling of undefined variable */
1989 } else if (rc < 0) {
1992 /* the preliminary result is the raw value of the variable.
1993 This may be modified by the operations that may follow. */
1994 tokenbuf_set(result, data, data + len, buffer_size);
1998 /* parse optional post-operations */
2001 tokenbuf_free(&tmp);
2002 tokenbuf_init(&tmp);
2004 while (p != end && *p == ':') {
2007 rc = parse_operation(var, ctx, p, end, result);
2009 rc = parse_operation(var, ctx, p, end, &tmp);
2016 if (p == end || *p != var->syntax.delim_close) {
2017 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2023 } else if (p[-1] == '+') {
2027 /* lazy handling of undefined variable */
2028 if (!failed && tokenbuf_isundef(result)) {
2029 if (ctx->force_expand) {
2030 rc = VAR_ERR_UNDEFINED_VARIABLE;
2034 tokenbuf_set(result, begin - 1, p, 0);
2037 /* return successfully */
2038 tokenbuf_free(&name);
2039 tokenbuf_free(&tmp);
2042 /* return with an error */
2044 tokenbuf_free(&name);
2045 tokenbuf_free(&tmp);
2046 tokenbuf_free(result);
2050 /* parse variable construct ("$name" or "${name...}") */
2053 var_t *var, var_parse_t *ctx,
2054 const char *begin, const char *end,
2059 int len, buffer_size;
2063 /* initialization */
2065 tokenbuf_init(result);
2067 /* parse init delimiter */
2068 if (p == end || *p != var->syntax.delim_init)
2072 return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
2074 /* parse a simple variable name.
2075 (if this fails, we're try to parse a complex variable construct) */
2076 rc = parse_name(var, ctx, p, end);
2080 inc = (p[rc] == '+');
2081 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
2082 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
2083 tokenbuf_set(result, begin, begin + 1 + rc, 0);
2088 tokenbuf_set(result, data, data + len, buffer_size);
2092 /* parse a complex variable construct (else case) */
2093 rc = parse_variable_complex(var, ctx, p, end, result);
2099 /* parse loop construct limits ("[...]{b,s,e}") */
2102 var_t *var, var_parse_t *ctx,
2103 const char *begin, const char *end,
2104 int *start, int *step, int *stop, int *open_stop)
2110 /* initialization */
2113 /* we are happy if nothing is to left to parse */
2117 /* parse start delimiter */
2118 if (*p != var->syntax.delim_open)
2122 /* parse loop start value */
2124 rc = parse_numexp(var, ctx, p, end, start, &failed);
2125 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2126 *start = 0; /* use default */
2128 return (var_rc_t)rc;
2132 return VAR_ERR_UNDEFINED_VARIABLE;
2134 /* parse separator */
2136 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2139 /* parse loop step value */
2141 rc = parse_numexp(var, ctx, p, end, step, &failed);
2142 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
2143 *step = 1; /* use default */
2145 return (var_rc_t)rc;
2149 return VAR_ERR_UNDEFINED_VARIABLE;
2151 /* parse separator */
2153 /* if not found, parse end delimiter */
2154 if (*p != var->syntax.delim_close)
2155 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2158 /* shift step value to stop value */
2162 /* determine whether loop end is open */
2167 return (var_rc_t)(p - begin);
2171 /* parse loop stop value */
2173 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2174 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2175 *stop = 0; /* use default */
2179 return (var_rc_t)rc;
2185 return VAR_ERR_UNDEFINED_VARIABLE;
2187 /* parse end delimiter */
2188 if (*p != var->syntax.delim_close)
2189 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2192 /* return amount of parsed input */
2193 return (var_rc_t)(p - begin);
2196 /* parse plain text */
2199 var_t *var, var_parse_t *ctx,
2200 const char *begin, const char *end)
2204 /* parse until delim_init (variable construct)
2205 or index_open (loop construct) is found */
2206 for (p = begin; p != end; p++) {
2207 if (*p == var->syntax.escape) {
2208 p++; /* skip next character */
2210 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2212 else if (*p == var->syntax.delim_init)
2214 else if ( var->syntax.index_open != EOS
2215 && ( *p == var->syntax.index_open
2216 || *p == var->syntax.index_close))
2222 /* expand input in general */
2225 var_t *var, var_parse_t *ctx,
2226 const char *begin, const char *end,
2227 tokenbuf_t *output, int recursion_level)
2232 int start, step, stop, open_stop;
2236 int loop_limit_length;
2239 /* initialization */
2243 /* try to parse a loop construct */
2245 && var->syntax.index_open != EOS
2246 && *p == var->syntax.index_open) {
2249 /* loop preparation */
2250 loop_limit_length = -1;
2251 rel_lookup_cnt = ctx->rel_lookup_cnt;
2259 /* iterate over loop construct, either as long as there is
2260 (still) nothing known about the limit, or there is an open
2261 (=unknown) limit stop and there are still defined variables
2262 or there is a stop limit known and it is still not reached */
2266 && ( loop_limit_length < 0
2267 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2272 /* remember current output end for restoring */
2273 output_backup = (output->end - output->begin);
2275 /* open temporary context for recursion */
2276 ctx = var_parse_push(ctx, &myctx);
2277 ctx->force_expand = 1;
2278 ctx->rel_lookup_flag = 1;
2279 ctx->index_this = i;
2281 /* recursive parse input through ourself */
2282 rc = parse_input(var, ctx, p, end,
2283 output, recursion_level+1);
2285 /* retrieve info and close temporary context */
2286 rel_lookup_cnt = ctx->rel_lookup_cnt;
2287 ctx = var_parse_pop(ctx);
2289 /* error handling */
2293 /* make sure the loop construct is closed */
2294 if (p[rc] != var->syntax.index_close) {
2295 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2299 /* try to parse loop construct limit specification */
2300 if (loop_limit_length < 0) {
2301 rc2 = parse_looplimits(var, ctx, p+rc+1, end,
2302 &start, &step, &stop, &open_stop);
2306 loop_limit_length = 0;
2308 loop_limit_length = rc2;
2309 /* restart loop from scratch */
2310 output->end = (output->begin + output_backup);
2316 /* if stop value is open, restore to the output end
2317 because the last iteration was just to determine the loop
2318 termination and its result has to be discarded */
2320 output->end = (output->begin + output_backup);
2322 /* skip parsed loop construct */
2325 p += loop_limit_length;
2330 /* try to parse plain text */
2331 rc = parse_text(var, ctx, p, end);
2333 if (!tokenbuf_append(output, p, rc)) {
2334 rc = VAR_ERR_OUT_OF_MEMORY;
2342 /* try to parse a variable construct */
2343 tokenbuf_init(&result);
2344 rc = parse_variable(var, ctx, p, end, &result);
2346 if (!tokenbuf_merge(output, &result)) {
2347 tokenbuf_free(&result);
2348 rc = VAR_ERR_OUT_OF_MEMORY;
2351 tokenbuf_free(&result);
2355 tokenbuf_free(&result);
2359 } while (p != end && rc > 0);
2361 /* We do not know whether this really could happen, but because we
2362 are paranoid, report an error at the outer most parsing level if
2363 there is still any input. Because this would mean that we are no
2364 longer able to parse the remaining input as a loop construct, a
2365 text or a variable construct. This would be very strange, but
2366 could perhaps happen in case of configuration errors!?... */
2367 if (recursion_level == 0 && p != end) {
2368 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2372 /* return amount of parsed text */
2373 return (var_rc_t)(p - begin);
2375 /* return with an error where as a special case the output begin is
2376 set to the input begin and the output end to the last input parsing
2379 tokenbuf_free(output);
2380 tokenbuf_set(output, begin, p, 0);
2381 return (var_rc_t)rc;
2386 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2390 /* create variable expansion context */
2398 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2399 if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
2400 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2401 memset(var, 0, sizeof(var));
2402 var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
2407 /* destroy variable expansion context */
2413 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2418 /* configure variable expansion context */
2429 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2432 case VAR_CONFIG_SYNTAX: {
2434 s = (var_syntax_t *)va_arg(ap, void *);
2436 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2437 var->syntax.escape = s->escape;
2438 var->syntax.delim_init = s->delim_init;
2439 var->syntax.delim_open = s->delim_open;
2440 var->syntax.delim_close = s->delim_close;
2441 var->syntax.index_open = s->index_open;
2442 var->syntax.index_close = s->index_close;
2443 var->syntax.index_mark = s->index_mark;
2444 var->syntax.name_chars = NULL; /* unused internally */
2445 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
2447 if ( var->syntax_nameclass[(int)var->syntax.delim_init]
2448 || var->syntax_nameclass[(int)var->syntax.delim_open]
2449 || var->syntax_nameclass[(int)var->syntax.delim_close]
2450 || var->syntax_nameclass[(int)var->syntax.escape])
2451 return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
2454 case VAR_CONFIG_CB_VALUE: {
2457 fct = (var_cb_value_t)va_arg(ap, void *);
2458 ctx = (void *)va_arg(ap, void *);
2459 var->cb_value_fct = fct;
2460 var->cb_value_ctx = ctx;
2463 case VAR_CONFIG_CB_OPERATION: {
2464 var_cb_operation_t fct;
2466 fct = (var_cb_operation_t)va_arg(ap, void *);
2467 ctx = (void *)va_arg(ap, void *);
2468 var->cb_operation_fct = fct;
2469 var->cb_operation_ctx = ctx;
2473 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2479 /* perform unescape operation on a buffer */
2483 const char *src, int srclen,
2484 char *dst, int dstlen,
2490 if (var == NULL || src == NULL || dst == NULL)
2491 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2496 return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2515 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
2518 case '0': case '1': case '2': case '3': case '4':
2519 case '5': case '6': case '7': case '8': case '9':
2521 && isdigit((int)src[1])
2522 && isdigit((int)src[2])) {
2523 if ((rc = expand_octal(&src, &dst, end)) != 0)
2541 /* perform expand operation on a buffer */
2545 const char *src_ptr, int src_len,
2546 char **dst_ptr, int *dst_len,
2553 /* argument sanity checks */
2554 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2555 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2557 /* prepare internal expansion context */
2559 ctx.force_expand = force_expand;
2560 ctx.rel_lookup_flag = 0;
2561 ctx.rel_lookup_cnt = 0;
2564 /* start the parsing */
2565 tokenbuf_init(&output);
2566 rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
2568 /* post-processing */
2570 /* always EOS-terminate output for convinience reasons
2571 but do not count the EOS-terminator in the length */
2572 if (!tokenbuf_append(&output, "\0", 1)) {
2573 tokenbuf_free(&output);
2574 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2578 /* provide result */
2579 *dst_ptr = (char *)output.begin;
2580 if (dst_len != NULL)
2581 *dst_len = (output.end - output.begin);
2585 /* provide result */
2586 if (dst_len != NULL)
2587 *dst_len = (output.end - output.begin);
2593 /* format and expand a string */
2597 char **dst_ptr, int force_expand,
2598 const char *fmt, va_list ap)
2605 /* argument sanity checks */
2606 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2607 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2609 /* determine formatting buffer length */
2611 nBuf = var_mvsnprintf(NULL, 0, fmt, ap);
2614 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2616 /* perform formatting */
2617 if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
2618 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2619 nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
2622 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2625 /* perform expansion */
2626 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
2637 /* format and expand a string */
2641 char **dst_ptr, int force_expand,
2642 const char *fmt, ...)
2647 /* argument sanity checks */
2648 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2649 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2652 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2658 /* var_rc_t to string mapping table */
2659 static const char *var_errors[] = {
2660 "everything ok", /* VAR_OK = 0 */
2661 "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2662 "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */
2663 "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */
2664 "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */
2665 "invalid octal value", /* VAR_ERR_INVALID_OCTAL */
2666 "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */
2667 "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
2668 "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */
2669 "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */
2670 "out of memory", /* VAR_ERR_OUT_OF_MEMORY */
2671 "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
2672 "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */
2673 "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
2674 "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
2675 "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */
2676 "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
2677 "invalid regex in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
2678 "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
2679 "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */
2680 "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */
2681 "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */
2682 "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2683 "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
2684 "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */
2685 "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2686 "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
2687 "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
2688 "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
2689 "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */
2690 "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */
2691 "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
2692 "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
2693 "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
2694 "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */
2695 "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2696 "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
2697 "index of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
2698 "index of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
2699 "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
2700 "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
2701 "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2702 "invalid character in loop limits", /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
2703 "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
2704 "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */
2705 "formatting failure" /* VAR_ERR_FORMATTING_FAILURE */
2708 /* translate a return code into its corresponding descriptive text */
2709 char *var_strerror(var_t *var, var_rc_t rc)
2712 rc = (var_rc_t)(0 - rc);
2713 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
2714 str = "unknown error";
2716 str = (char *)var_errors[rc];