1 /*
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/)
6 *
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/.
9 *
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
13 * copies.
14 *
15 * For disclaimer see below.
16 */
17 /*
18 * Adapted by Kern Sibbald to BACULA June 2003
19 */
20 /* check-sources:disable-copyright-check */
21
22 #include "include/bareos.h"
23 #if defined(HAVE_PCREPOSIX)
24 # include <pcreposix.h>
25 #elif defined(HAVE_WIN32)
26 # include "bregex.h"
27 #else
28 # include <regex.h>
29 #endif
30 #include "var.h"
31
32 /* support for OSSP ex based exception throwing */
33 #ifdef WITH_EX
34 # include "ex.h"
35 # define VAR_RC(rv) \
36 ((rv) != VAR_OK && (ex_catching && !ex_shielding) \
37 ? (ex_throw(var_id, NULL, (rv)), (rv)) \
38 : (rv))
39 #else
40 # define VAR_RC(rv) (rv)
41 #endif /* WITH_EX */
42
43 #ifndef EOS
44 # define EOS '\0'
45 #endif
46
47 /*
48 **
49 ** ==== INTERNAL DATA STRUCTURES ====
50 **
51 */
52
53 typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
54
55 /* the external context structure */
56 struct var_st {
57 var_syntax_t syntax;
58 char_class_t syntax_nameclass;
59 var_cb_value_t cb_value_fct;
60 void* cb_value_ctx;
61 var_cb_operation_t cb_operation_fct;
62 void* cb_operation_ctx;
63 };
64
65 /* the internal expansion context structure */
66 struct var_parse_st {
67 struct var_parse_st* lower;
68 int force_expand;
69 int rel_lookup_flag;
70 int rel_lookup_cnt;
71 int index_this;
72 };
73 typedef struct var_parse_st var_parse_t;
74
75 /* the default syntax configuration */
76 static const var_syntax_t var_syntax_default = {
77 '\\', /* escape */
78 '$', /* delim_init */
79 '{', /* delim_open */
80 '}', /* delim_close */
81 '[', /* index_open */
82 ']', /* index_close */
83 '#', /* index_mark */
84 "a-zA-Z0-9_" /* name_chars */
85 };
86
87 /*
88 **
89 ** ==== FORMATTING FUNCTIONS ====
90 **
91 */
92
93 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only
94 */
var_mvxprintf(int (* output)(void * ctx,const char * buffer,int bufsize),void * ctx,const char * format,va_list ap)95 static int var_mvxprintf(int (*output)(void* ctx,
96 const char* buffer,
97 int bufsize),
98 void* ctx,
99 const char* format,
100 va_list ap)
101 {
102 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
103 char ibuf[((sizeof(int) * 8) / 3) + 10];
104 const char* cp;
105 char c;
106 int d;
107 int n;
108 int bytes;
109
110 if (format == NULL) return -1;
111 bytes = 0;
112 while (*format != '\0') {
113 if (*format == '%') {
114 c = *(format + 1);
115 if (c == '%') {
116 /* expand "%%" */
117 cp = &c;
118 n = sizeof(char);
119 } else if (c == 'c') {
120 /* expand "%c" */
121 c = (char)va_arg(ap, int);
122 cp = &c;
123 n = sizeof(char);
124 } else if (c == 's') {
125 /* expand "%s" */
126 if ((cp = (char*)va_arg(ap, char*)) == NULL) cp = "(null)";
127 n = strlen(cp);
128 } else if (c == 'd') {
129 /* expand "%d" */
130 d = (int)va_arg(ap, int);
131 Bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
132 cp = ibuf;
133 n = strlen(cp);
134 } else {
135 /* any other "%X" */
136 cp = (char*)format;
137 n = 2;
138 }
139 format += 2;
140 } else {
141 /* plain text */
142 cp = (char*)format;
143 if ((format = strchr(cp, '%')) == NULL) format = strchr(cp, '\0');
144 n = format - cp;
145 }
146 /* perform output operation */
147 if (output != NULL)
148 if ((n = output(ctx, cp, n)) == -1) break;
149 bytes += n;
150 }
151 return bytes;
152 }
153
154 /* output callback function context for var_mvsnprintf() */
155 typedef struct {
156 char* bufptr;
157 int buflen;
158 } var_mvsnprintf_cb_t;
159
160 /* output callback function for var_mvsnprintf() */
var_mvsnprintf_cb(void * _ctx,const char * buffer,int bufsize)161 static int var_mvsnprintf_cb(void* _ctx, const char* buffer, int bufsize)
162 {
163 var_mvsnprintf_cb_t* ctx = (var_mvsnprintf_cb_t*)_ctx;
164
165 if (bufsize > ctx->buflen) return -1;
166 memcpy(ctx->bufptr, buffer, bufsize);
167 ctx->bufptr += bufsize;
168 ctx->buflen -= bufsize;
169 return bufsize;
170 }
171
172 /* minimal vsnprintf(3) variant which supports %{c,s,d} only */
var_mvsnprintf(char * buffer,int bufsize,const char * format,va_list ap)173 static int var_mvsnprintf(char* buffer,
174 int bufsize,
175 const char* format,
176 va_list ap)
177 {
178 int n;
179 var_mvsnprintf_cb_t ctx;
180
181 if (format == NULL) return -1;
182 if (buffer != NULL && bufsize == 0) return -1;
183 if (buffer == NULL) /* just determine output length */
184 n = var_mvxprintf(NULL, NULL, format, ap);
185 else {
186 /* perform real output */
187 ctx.bufptr = buffer;
188 ctx.buflen = bufsize;
189 n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
190 if (n != -1 && ctx.buflen == 0) n = -1;
191 if (n != -1) *(ctx.bufptr) = '\0';
192 }
193 return n;
194 }
195
196 /*
197 **
198 ** ==== PARSE CONTEXT FUNCTIONS ====
199 **
200 */
201
var_parse_push(var_parse_t * lower,var_parse_t * upper)202 static var_parse_t* var_parse_push(var_parse_t* lower, var_parse_t* upper)
203 {
204 if (upper == NULL) return NULL;
205 memcpy(upper, lower, sizeof(var_parse_t));
206 upper->lower = lower;
207 return upper;
208 }
209
var_parse_pop(var_parse_t * upper)210 static var_parse_t* var_parse_pop(var_parse_t* upper)
211 {
212 if (upper == NULL) return NULL;
213 return upper->lower;
214 }
215
216 /*
217 **
218 ** ==== TOKEN BUFFER FUNCTIONS ====
219 **
220 */
221
222 #define TOKENBUF_INITIAL_BUFSIZE 64
223
224 typedef struct {
225 const char* begin;
226 const char* end;
227 int buffer_size;
228 } tokenbuf_t;
229
tokenbuf_init(tokenbuf_t * buf)230 static void tokenbuf_init(tokenbuf_t* buf)
231 {
232 buf->begin = NULL;
233 buf->end = NULL;
234 buf->buffer_size = 0;
235 return;
236 }
237
tokenbuf_isundef(tokenbuf_t * buf)238 static int tokenbuf_isundef(tokenbuf_t* buf)
239 {
240 if (buf->begin == NULL && buf->end == NULL) return 1;
241 return 0;
242 }
243
tokenbuf_isempty(tokenbuf_t * buf)244 static int tokenbuf_isempty(tokenbuf_t* buf)
245 {
246 if (buf->begin == buf->end) return 1;
247 return 0;
248 }
249
tokenbuf_set(tokenbuf_t * buf,const char * begin,const char * end,int buffer_size)250 static void tokenbuf_set(tokenbuf_t* buf,
251 const char* begin,
252 const char* end,
253 int buffer_size)
254 {
255 buf->begin = begin;
256 buf->end = end;
257 buf->buffer_size = buffer_size;
258 return;
259 }
260
tokenbuf_move(tokenbuf_t * src,tokenbuf_t * dst)261 static void tokenbuf_move(tokenbuf_t* src, tokenbuf_t* dst)
262 {
263 dst->begin = src->begin;
264 dst->end = src->end;
265 dst->buffer_size = src->buffer_size;
266 tokenbuf_init(src);
267 return;
268 }
269
tokenbuf_assign(tokenbuf_t * buf,const char * data,int len)270 static int tokenbuf_assign(tokenbuf_t* buf, const char* data, int len)
271 {
272 char* p;
273
274 if ((p = (char*)malloc(len + 1)) == NULL) return 0;
275 memcpy(p, data, len);
276 buf->begin = p;
277 buf->end = p + len;
278 buf->buffer_size = len + 1;
279 *((char*)(buf->end)) = EOS;
280 return 1;
281 }
282
tokenbuf_append(tokenbuf_t * output,const char * data,int len)283 static int tokenbuf_append(tokenbuf_t* output, const char* data, int len)
284 {
285 char* new_buffer;
286 int new_size;
287 char* tmp;
288
289 /* Is the tokenbuffer initialized at all? If not, allocate a
290 standard-sized buffer to begin with. */
291 if (output->begin == NULL) {
292 if ((output->begin = output->end
293 = (const char*)malloc(TOKENBUF_INITIAL_BUFSIZE))
294 == NULL)
295 return 0;
296 output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
297 }
298
299 /* does the token contain text, but no buffer has been allocated yet? */
300 if (output->buffer_size == 0) {
301 /* check whether data borders to output. If, we can append
302 simly by increasing the end pointer. */
303 if (output->end == data) {
304 output->end += len;
305 return 1;
306 }
307 /* ok, so copy the contents of output into an allocated buffer
308 so that we can append that way. */
309 if ((tmp = (char*)malloc(output->end - output->begin + len + 1)) == NULL)
310 return 0;
311 memcpy(tmp, output->begin, output->end - output->begin);
312 output->buffer_size = output->end - output->begin;
313 output->begin = tmp;
314 output->end = tmp + output->buffer_size;
315 output->buffer_size += len + 1;
316 }
317
318 /* does the token fit into the current buffer? If not, realloc a
319 larger buffer that fits. */
320 if ((output->buffer_size - (output->end - output->begin)) <= len) {
321 new_size = output->buffer_size;
322 do {
323 new_size *= 2;
324 } while ((new_size - (output->end - output->begin)) <= len);
325 if ((new_buffer = (char*)realloc((char*)output->begin, new_size)) == NULL)
326 return 0;
327 output->end = new_buffer + (output->end - output->begin);
328 output->begin = new_buffer;
329 output->buffer_size = new_size;
330 }
331
332 /* append the data at the end of the current buffer. */
333 if (len > 0) memcpy((char*)output->end, data, len);
334 output->end += len;
335 *((char*)output->end) = EOS;
336 return 1;
337 }
338
tokenbuf_merge(tokenbuf_t * output,tokenbuf_t * input)339 static int tokenbuf_merge(tokenbuf_t* output, tokenbuf_t* input)
340 {
341 return tokenbuf_append(output, input->begin, input->end - input->begin);
342 }
343
tokenbuf_free(tokenbuf_t * buf)344 static void tokenbuf_free(tokenbuf_t* buf)
345 {
346 if (buf->begin != NULL && buf->buffer_size > 0) free((char*)buf->begin);
347 buf->begin = buf->end = NULL;
348 buf->buffer_size = 0;
349 return;
350 }
351
352 /*
353 **
354 ** ==== CHARACTER CLASS EXPANSION ====
355 **
356 */
357
expand_range(char a,char b,char_class_t chrclass)358 static void expand_range(char a, char b, char_class_t chrclass)
359 {
360 do {
361 chrclass[(int)a] = 1;
362 } while (++a <= b);
363 return;
364 }
365
expand_character_class(const char * desc,char_class_t chrclass)366 static var_rc_t expand_character_class(const char* desc, char_class_t chrclass)
367 {
368 int i;
369
370 /* clear the class array. */
371 for (i = 0; i < 256; ++i) chrclass[i] = 0;
372
373 /* walk through class description and set appropriate entries in array */
374 while (*desc != EOS) {
375 if (desc[1] == '-' && desc[2] != EOS) {
376 if (desc[0] > desc[2]) return VAR_ERR_INCORRECT_CLASS_SPEC;
377 expand_range(desc[0], desc[2], chrclass);
378 desc += 3;
379 } else {
380 chrclass[(int)*desc] = 1;
381 desc++;
382 }
383 }
384 return VAR_OK;
385 }
386
387 /*
388 **
389 ** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
390 **
391 */
392
expand_isoct(int c)393 static int expand_isoct(int c)
394 {
395 if (c >= '0' && c <= '7')
396 return 1;
397 else
398 return 0;
399 }
400
expand_octal(const char ** src,char ** dst,const char * end)401 static var_rc_t expand_octal(const char** src, char** dst, const char* end)
402 {
403 int c;
404
405 if (end - *src < 3) return VAR_ERR_INCOMPLETE_OCTAL;
406 if (!expand_isoct(**src) || !expand_isoct((*src)[1])
407 || !expand_isoct((*src)[2]))
408 return VAR_ERR_INVALID_OCTAL;
409
410 c = **src - '0';
411 if (c > 3) return VAR_ERR_OCTAL_TOO_LARGE;
412 c *= 8;
413 (*src)++;
414
415 c += **src - '0';
416 c *= 8;
417 (*src)++;
418
419 c += **src - '0';
420
421 **dst = (char)c;
422 (*dst)++;
423 return VAR_OK;
424 }
425
expand_ishex(int c)426 static int expand_ishex(int c)
427 {
428 if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
429 || (c >= 'A' && c <= 'F'))
430 return 1;
431 else
432 return 0;
433 }
434
expand_simple_hex(const char ** src,char ** dst,const char * end)435 static var_rc_t expand_simple_hex(const char** src, char** dst, const char* end)
436 {
437 int c = 0;
438
439 if (end - *src < 2) return VAR_ERR_INCOMPLETE_HEX;
440 if (!expand_ishex(**src) || !expand_ishex((*src)[1]))
441 return VAR_ERR_INVALID_HEX;
442
443 if (**src >= '0' && **src <= '9')
444 c = **src - '0';
445 else if (**src >= 'a' && **src <= 'f')
446 c = **src - 'a' + 10;
447 else if (**src >= 'A' && **src <= 'F')
448 c = **src - 'A' + 10;
449
450 c = c << 4;
451 (*src)++;
452
453 if (**src >= '0' && **src <= '9')
454 c += **src - '0';
455 else if (**src >= 'a' && **src <= 'f')
456 c += **src - 'a' + 10;
457 else if (**src >= 'A' && **src <= 'F')
458 c += **src - 'A' + 10;
459
460 **dst = (char)c;
461 (*dst)++;
462 return VAR_OK;
463 }
464
expand_grouped_hex(const char ** src,char ** dst,const char * end)465 static var_rc_t expand_grouped_hex(const char** src,
466 char** dst,
467 const char* end)
468 {
469 var_rc_t rc;
470
471 while (*src < end && **src != '}') {
472 if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK) return rc;
473 (*src)++;
474 }
475 if (*src == end) return VAR_ERR_INCOMPLETE_GROUPED_HEX;
476
477 return VAR_OK;
478 }
479
expand_hex(const char ** src,char ** dst,const char * end)480 static var_rc_t expand_hex(const char** src, char** dst, const char* end)
481 {
482 if (*src == end) return VAR_ERR_INCOMPLETE_HEX;
483 if (**src == '{') {
484 (*src)++;
485 return expand_grouped_hex(src, dst, end);
486 } else
487 return expand_simple_hex(src, dst, end);
488 }
489
490 /*
491 **
492 ** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
493 **
494 */
495
496 /* forward declarations */
497 static int ParseVariable(var_t* var,
498 var_parse_t* ctx,
499 const char* begin,
500 const char* end,
501 tokenbuf_t* result);
502 static int parse_numexp(var_t* var,
503 var_parse_t* ctx,
504 const char* begin,
505 const char* end,
506 int* result,
507 int* failed);
508 static int parse_name(var_t* var,
509 var_parse_t* ctx,
510 const char* begin,
511 const char* end);
512
513 /* parse pattern text */
parse_pattern(var_t * var,var_parse_t * ctx,const char * begin,const char * end)514 static int parse_pattern(var_t* var,
515 var_parse_t* ctx,
516 const char* begin,
517 const char* end)
518 {
519 const char* p;
520
521 /* parse until '/' */
522 for (p = begin; p != end && *p != '/'; p++) {
523 if (*p == var->syntax.escape) {
524 if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
525 p++;
526 }
527 }
528 return (p - begin);
529 }
530
531 /* parse substitution text */
parse_substext(var_t * var,var_parse_t * ctx,const char * begin,const char * end)532 static int parse_substext(var_t* var,
533 var_parse_t* ctx,
534 const char* begin,
535 const char* end)
536 {
537 const char* p;
538
539 /* parse until delim_init or '/' */
540 for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
541 if (*p == var->syntax.escape) {
542 if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
543 p++;
544 }
545 }
546 return (p - begin);
547 }
548
549 /* parse expression text */
parse_exptext(var_t * var,var_parse_t * ctx,const char * begin,const char * end)550 static int parse_exptext(var_t* var,
551 var_parse_t* ctx,
552 const char* begin,
553 const char* end)
554 {
555 const char* p;
556
557 /* parse until delim_init or delim_close or ':' */
558 for (p = begin; p != end && *p != var->syntax.delim_init
559 && *p != var->syntax.delim_close && *p != ':';
560 p++) {
561 if (*p == var->syntax.escape) {
562 if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
563 p++;
564 }
565 }
566 return (p - begin);
567 }
568
569 /* parse opertion argument text */
parse_opargtext(var_t * var,var_parse_t * ctx,const char * begin,const char * end)570 static int parse_opargtext(var_t* var,
571 var_parse_t* ctx,
572 const char* begin,
573 const char* end)
574 {
575 const char* p;
576
577 /* parse until delim_init or ')' */
578 for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
579 if (*p == var->syntax.escape) {
580 if (p + 1 == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
581 p++;
582 }
583 }
584 return (p - begin);
585 }
586
parse_opargtext_or_variable(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * result)587 static int parse_opargtext_or_variable(var_t* var,
588 var_parse_t* ctx,
589 const char* begin,
590 const char* end,
591 tokenbuf_t* result)
592 {
593 const char* p;
594 tokenbuf_t tmp;
595 int rc;
596
597 tokenbuf_init(result);
598 tokenbuf_init(&tmp);
599 p = begin;
600 if (p == end) return 0;
601 do {
602 rc = parse_opargtext(var, ctx, p, end);
603 if (rc < 0) goto error_return;
604 if (rc > 0) {
605 if (!tokenbuf_append(result, p, rc)) {
606 rc = VAR_ERR_OUT_OF_MEMORY;
607 goto error_return;
608 }
609 p += rc;
610 }
611 rc = ParseVariable(var, ctx, p, end, &tmp);
612 if (rc < 0) goto error_return;
613 if (rc > 0) {
614 p += rc;
615 if (!tokenbuf_merge(result, &tmp)) {
616 rc = VAR_ERR_OUT_OF_MEMORY;
617 goto error_return;
618 }
619 }
620 tokenbuf_free(&tmp); /* KES 11/9/2003 */
621 } while (rc > 0);
622 tokenbuf_free(&tmp);
623 return (p - begin);
624
625 error_return:
626 tokenbuf_free(&tmp);
627 tokenbuf_free(result);
628 return rc;
629 }
630
631 /* parse expression or variable */
parse_exptext_or_variable(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * result)632 static int parse_exptext_or_variable(var_t* var,
633 var_parse_t* ctx,
634 const char* begin,
635 const char* end,
636 tokenbuf_t* result)
637 {
638 const char* p = begin;
639 tokenbuf_t tmp;
640 int rc;
641
642 tokenbuf_init(result);
643 tokenbuf_init(&tmp);
644 if (begin == end) return 0;
645 do {
646 /* try to parse expression text */
647 rc = parse_exptext(var, ctx, p, end);
648 if (rc < 0) goto error_return;
649 if (rc > 0) {
650 if (!tokenbuf_append(result, p, rc)) {
651 rc = VAR_ERR_OUT_OF_MEMORY;
652 goto error_return;
653 }
654 p += rc;
655 }
656
657 /* try to parse variable construct */
658 rc = ParseVariable(var, ctx, p, end, &tmp);
659 if (rc < 0) goto error_return;
660 if (rc > 0) {
661 p += rc;
662 if (!tokenbuf_merge(result, &tmp)) {
663 rc = VAR_ERR_OUT_OF_MEMORY;
664 goto error_return;
665 }
666 }
667 tokenbuf_free(&tmp); /* KES 11/9/2003 */
668 } while (rc > 0);
669
670 tokenbuf_free(&tmp);
671 return (p - begin);
672
673 error_return:
674 tokenbuf_free(&tmp);
675 tokenbuf_free(result);
676 return rc;
677 }
678
679 /* parse substitution text or variable */
parse_substext_or_variable(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * result)680 static int parse_substext_or_variable(var_t* var,
681 var_parse_t* ctx,
682 const char* begin,
683 const char* end,
684 tokenbuf_t* result)
685 {
686 const char* p = begin;
687 tokenbuf_t tmp;
688 int rc;
689
690 tokenbuf_init(result);
691 tokenbuf_init(&tmp);
692 if (begin == end) return 0;
693 do {
694 /* try to parse substitution text */
695 rc = parse_substext(var, ctx, p, end);
696 if (rc < 0) goto error_return;
697 if (rc > 0) {
698 if (!tokenbuf_append(result, p, rc)) {
699 rc = VAR_ERR_OUT_OF_MEMORY;
700 goto error_return;
701 }
702 p += rc;
703 }
704
705 /* try to parse substitution text */
706 rc = ParseVariable(var, ctx, p, end, &tmp);
707 if (rc < 0) goto error_return;
708 if (rc > 0) {
709 p += rc;
710 if (!tokenbuf_merge(result, &tmp)) {
711 rc = VAR_ERR_OUT_OF_MEMORY;
712 goto error_return;
713 }
714 }
715 tokenbuf_free(&tmp); /* KES 11/9/2003 */
716 } while (rc > 0);
717
718 tokenbuf_free(&tmp);
719 return (p - begin);
720
721 error_return:
722 tokenbuf_free(&tmp);
723 tokenbuf_free(result);
724 return rc;
725 }
726
727 /* parse class description */
parse_class_description(var_t * var,var_parse_t * ctx,tokenbuf_t * src,tokenbuf_t * dst)728 static int parse_class_description(var_t* var,
729 var_parse_t* ctx,
730 tokenbuf_t* src,
731 tokenbuf_t* dst)
732 {
733 unsigned char c, d;
734 const char* p;
735
736 p = src->begin;
737 while (p != src->end) {
738 if ((src->end - p) >= 3 && p[1] == '-') {
739 if (*p > p[2]) return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
740 for (c = *p, d = p[2]; c <= d; ++c) {
741 if (!tokenbuf_append(dst, (char*)&c, 1)) return VAR_ERR_OUT_OF_MEMORY;
742 }
743 p += 3;
744 } else {
745 if (!tokenbuf_append(dst, p, 1)) return VAR_ERR_OUT_OF_MEMORY;
746 p++;
747 }
748 }
749 return VAR_OK;
750 }
751
752 /* parse regex replace part */
parse_regex_replace(var_t * var,var_parse_t * ctx,const char * data,tokenbuf_t * orig,regmatch_t * pmatch,tokenbuf_t * expanded)753 static int parse_regex_replace(var_t* var,
754 var_parse_t* ctx,
755 const char* data,
756 tokenbuf_t* orig,
757 regmatch_t* pmatch,
758 tokenbuf_t* expanded)
759 {
760 const char* p;
761 int i;
762
763 p = orig->begin;
764 tokenbuf_init(expanded);
765
766 while (p != orig->end) {
767 if (*p == '\\') {
768 if (orig->end - p <= 1) {
769 tokenbuf_free(expanded);
770 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
771 }
772 p++;
773 if (*p == '\\') {
774 if (!tokenbuf_append(expanded, p, 1)) {
775 tokenbuf_free(expanded);
776 return VAR_ERR_OUT_OF_MEMORY;
777 }
778 p++;
779 continue;
780 }
781 if (!isdigit((int)*p)) {
782 tokenbuf_free(expanded);
783 return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
784 }
785 i = (*p - '0');
786 p++;
787 if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
788 tokenbuf_free(expanded);
789 return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
790 }
791 if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
792 pmatch[i].rm_eo - pmatch[i].rm_so)) {
793 tokenbuf_free(expanded);
794 return VAR_ERR_OUT_OF_MEMORY;
795 }
796 } else {
797 if (!tokenbuf_append(expanded, p, 1)) {
798 tokenbuf_free(expanded);
799 return VAR_ERR_OUT_OF_MEMORY;
800 }
801 p++;
802 }
803 }
804
805 return VAR_OK;
806 }
807
808 /* operation: transpose */
op_transpose(var_t * var,var_parse_t * ctx,tokenbuf_t * data,tokenbuf_t * search,tokenbuf_t * replace)809 static int op_transpose(var_t* var,
810 var_parse_t* ctx,
811 tokenbuf_t* data,
812 tokenbuf_t* search,
813 tokenbuf_t* replace)
814 {
815 tokenbuf_t srcclass, dstclass;
816 const char* p;
817 int rc;
818 int i;
819
820 tokenbuf_init(&srcclass);
821 tokenbuf_init(&dstclass);
822 if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
823 goto error_return;
824 if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
825 goto error_return;
826 if (srcclass.begin == srcclass.end) {
827 rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
828 goto error_return;
829 }
830 if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
831 rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
832 goto error_return;
833 }
834 if (data->buffer_size == 0) {
835 tokenbuf_t tmp;
836 if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
837 rc = VAR_ERR_OUT_OF_MEMORY;
838 goto error_return;
839 }
840 tokenbuf_move(&tmp, data);
841 }
842 for (p = data->begin; p != data->end; ++p) {
843 for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
844 if (*p == srcclass.begin[i]) {
845 *((char*)p) = dstclass.begin[i];
846 break;
847 }
848 }
849 }
850 tokenbuf_free(&srcclass);
851 tokenbuf_free(&dstclass);
852 return VAR_OK;
853
854 error_return:
855 tokenbuf_free(search);
856 tokenbuf_free(replace);
857 tokenbuf_free(&srcclass);
858 tokenbuf_free(&dstclass);
859 return rc;
860 }
861
862 /* operation: search & replace */
op_search_and_replace(var_t * var,var_parse_t * ctx,tokenbuf_t * data,tokenbuf_t * search,tokenbuf_t * replace,tokenbuf_t * flags)863 static int op_search_and_replace(var_t* var,
864 var_parse_t* ctx,
865 tokenbuf_t* data,
866 tokenbuf_t* search,
867 tokenbuf_t* replace,
868 tokenbuf_t* flags)
869 {
870 tokenbuf_t tmp;
871 const char* p;
872 int case_insensitive = 0;
873 int multiline = 0;
874 int global = 0;
875 int no_regex = 0;
876 bool rc;
877
878 if (search->begin == search->end) return VAR_ERR_EMPTY_SEARCH_STRING;
879
880 for (p = flags->begin; p != flags->end; p++) {
881 switch (tolower(*p)) {
882 case 'm':
883 multiline = 1;
884 break;
885 case 'i':
886 case_insensitive = 1;
887 break;
888 case 'g':
889 global = 1;
890 break;
891 case 't':
892 no_regex = 1;
893 break;
894 default:
895 return VAR_ERR_UNKNOWN_REPLACE_FLAG;
896 }
897 }
898
899 if (no_regex) {
900 /* plain text pattern based operation */
901 tokenbuf_init(&tmp);
902 for (p = data->begin; p != data->end;) {
903 if (case_insensitive)
904 rc = bstrncasecmp(p, search->begin, search->end - search->begin);
905 else
906 rc = bstrncmp(p, search->begin, search->end - search->begin);
907 if (!rc) {
908 /* not matched, copy character */
909 if (!tokenbuf_append(&tmp, p, 1)) {
910 tokenbuf_free(&tmp);
911 return VAR_ERR_OUT_OF_MEMORY;
912 }
913 p++;
914 } else {
915 /* matched, copy replacement string */
916 tokenbuf_merge(&tmp, replace);
917 p += (search->end - search->begin);
918 if (!global) {
919 /* append remaining text */
920 if (!tokenbuf_append(&tmp, p, data->end - p)) {
921 tokenbuf_free(&tmp);
922 return VAR_ERR_OUT_OF_MEMORY;
923 }
924 break;
925 }
926 }
927 }
928 tokenbuf_free(data);
929 tokenbuf_move(&tmp, data);
930 } else {
931 /* regular expression pattern based operation */
932 tokenbuf_t mydata;
933 tokenbuf_t myreplace;
934 regex_t preg{};
935 regmatch_t pmatch[10]{};
936 int regexec_flag;
937
938 /* copy pattern and data to own buffer to make sure they are EOS-terminated
939 */
940 if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
941 return VAR_ERR_OUT_OF_MEMORY;
942 if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
943 tokenbuf_free(&tmp);
944 return VAR_ERR_OUT_OF_MEMORY;
945 }
946
947 /* compile the pattern. */
948 rc = regcomp(&preg, tmp.begin,
949 (REG_EXTENDED | (multiline ? REG_NEWLINE : 0)
950 | (case_insensitive ? REG_ICASE : 0)));
951 tokenbuf_free(&tmp);
952 if (rc != 0) {
953 tokenbuf_free(&mydata);
954 return VAR_ERR_INVALID_REGEX_IN_REPLACE;
955 }
956
957 /* match the pattern and create the result string in the tmp buffer */
958 tokenbuf_append(&tmp, "", 0);
959 for (p = mydata.begin; p < mydata.end;) {
960 if (p == mydata.begin || p[-1] == '\n')
961 regexec_flag = 0;
962 else
963 regexec_flag = REG_NOTBOL;
964 rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch,
965 regexec_flag);
966 if (rc != 0) {
967 /* no (more) matching */
968 tokenbuf_append(&tmp, p, mydata.end - p);
969 break;
970 } else if (multiline && (p + pmatch[0].rm_so) == mydata.end
971 && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
972 /* special case: found empty pattern (usually /^/ or /$/ only)
973 in multi-line at end of data (after the last newline) */
974 tokenbuf_append(&tmp, p, mydata.end - p);
975 break;
976 } else {
977 /* append prolog string */
978 if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
979 regfree(&preg);
980 tokenbuf_free(&tmp);
981 tokenbuf_free(&mydata);
982 return VAR_ERR_OUT_OF_MEMORY;
983 }
984 /* create replace string */
985 rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
986 if (rc != VAR_OK) {
987 regfree(&preg);
988 tokenbuf_free(&tmp);
989 tokenbuf_free(&mydata);
990 return rc;
991 }
992 /* append replace string */
993 if (!tokenbuf_merge(&tmp, &myreplace)) {
994 regfree(&preg);
995 tokenbuf_free(&tmp);
996 tokenbuf_free(&mydata);
997 tokenbuf_free(&myreplace);
998 return VAR_ERR_OUT_OF_MEMORY;
999 }
1000 tokenbuf_free(&myreplace);
1001 /* skip now processed data */
1002 p += pmatch[0].rm_eo;
1003 /* if pattern matched an empty part (think about
1004 anchor-only regular expressions like /^/ or /$/) we
1005 skip the next character to make sure we do not enter
1006 an infinitive loop in matching */
1007 if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
1008 if (p >= mydata.end) break;
1009 if (!tokenbuf_append(&tmp, p, 1)) {
1010 regfree(&preg);
1011 tokenbuf_free(&tmp);
1012 tokenbuf_free(&mydata);
1013 return VAR_ERR_OUT_OF_MEMORY;
1014 }
1015 p++;
1016 }
1017 /* append prolog string and stop processing if we
1018 do not perform the search & replace globally */
1019 if (!global) {
1020 if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
1021 regfree(&preg);
1022 tokenbuf_free(&tmp);
1023 tokenbuf_free(&mydata);
1024 return VAR_ERR_OUT_OF_MEMORY;
1025 }
1026 break;
1027 }
1028 }
1029 }
1030 regfree(&preg);
1031 tokenbuf_free(data);
1032 tokenbuf_move(&tmp, data);
1033 tokenbuf_free(&mydata);
1034 }
1035
1036 return VAR_OK;
1037 }
1038
1039 /* operation: offset substring */
op_offset(var_t * var,var_parse_t * ctx,tokenbuf_t * data,int num1,int num2,int isrange)1040 static int op_offset(var_t* var,
1041 var_parse_t* ctx,
1042 tokenbuf_t* data,
1043 int num1,
1044 int num2,
1045 int isrange)
1046 {
1047 tokenbuf_t res;
1048 const char* p;
1049
1050 /* determine begin of result string */
1051 if ((data->end - data->begin) < num1) return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
1052 p = data->begin + num1;
1053
1054 /* if num2 is zero, we copy the rest from there. */
1055 if (num2 == 0) {
1056 if (!tokenbuf_assign(&res, p, data->end - p)) return VAR_ERR_OUT_OF_MEMORY;
1057 } else {
1058 /* ok, then use num2. */
1059 if (isrange) {
1060 if ((p + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1061 if (!tokenbuf_assign(&res, p, num2)) return VAR_ERR_OUT_OF_MEMORY;
1062 } else {
1063 if (num2 < num1) return VAR_ERR_OFFSET_LOGIC;
1064 if ((data->begin + num2) > data->end) return VAR_ERR_RANGE_OUT_OF_BOUNDS;
1065 if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
1066 return VAR_ERR_OUT_OF_MEMORY;
1067 }
1068 }
1069 tokenbuf_free(data);
1070 tokenbuf_move(&res, data);
1071 return VAR_OK;
1072 }
1073
1074 /* operation: padding */
op_padding(var_t * var,var_parse_t * ctx,tokenbuf_t * data,int width,tokenbuf_t * fill,char position)1075 static int op_padding(var_t* var,
1076 var_parse_t* ctx,
1077 tokenbuf_t* data,
1078 int width,
1079 tokenbuf_t* fill,
1080 char position)
1081 {
1082 tokenbuf_t result;
1083 int i;
1084
1085 if (fill->begin == fill->end) return VAR_ERR_EMPTY_PADDING_FILL_STRING;
1086 tokenbuf_init(&result);
1087 if (position == 'l') {
1088 /* left padding */
1089 i = width - (data->end - data->begin);
1090 if (i > 0) {
1091 i = i / (fill->end - fill->begin);
1092 while (i > 0) {
1093 if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
1094 return VAR_ERR_OUT_OF_MEMORY;
1095 i--;
1096 }
1097 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1098 if (!tokenbuf_append(data, fill->begin, i)) return VAR_ERR_OUT_OF_MEMORY;
1099 }
1100 } else if (position == 'r') {
1101 /* right padding */
1102 i = width - (data->end - data->begin);
1103 if (i > 0) {
1104 i = i / (fill->end - fill->begin);
1105 while (i > 0) {
1106 if (!tokenbuf_merge(&result, fill)) {
1107 tokenbuf_free(&result);
1108 return VAR_ERR_OUT_OF_MEMORY;
1109 }
1110 i--;
1111 }
1112 i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
1113 if (!tokenbuf_append(&result, fill->begin, i)) {
1114 tokenbuf_free(&result);
1115 return VAR_ERR_OUT_OF_MEMORY;
1116 }
1117 if (!tokenbuf_merge(&result, data)) {
1118 tokenbuf_free(&result);
1119 return VAR_ERR_OUT_OF_MEMORY;
1120 }
1121 /* move string from temporary buffer to data buffer */
1122 tokenbuf_free(data);
1123 tokenbuf_move(&result, data);
1124 }
1125 } else if (position == 'c') {
1126 /* centered padding */
1127 i = (width - (data->end - data->begin)) / 2;
1128 if (i > 0) {
1129 /* create the prefix */
1130 i = i / (fill->end - fill->begin);
1131 while (i > 0) {
1132 if (!tokenbuf_merge(&result, fill)) {
1133 tokenbuf_free(&result);
1134 return VAR_ERR_OUT_OF_MEMORY;
1135 }
1136 i--;
1137 }
1138 i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin);
1139 if (!tokenbuf_append(&result, fill->begin, i)) {
1140 tokenbuf_free(&result);
1141 return VAR_ERR_OUT_OF_MEMORY;
1142 }
1143 /* append the actual data string */
1144 if (!tokenbuf_merge(&result, data)) {
1145 tokenbuf_free(&result);
1146 return VAR_ERR_OUT_OF_MEMORY;
1147 }
1148 /* append the suffix */
1149 i = width - (result.end - result.begin);
1150 i = i / (fill->end - fill->begin);
1151 while (i > 0) {
1152 if (!tokenbuf_merge(&result, fill)) {
1153 tokenbuf_free(&result);
1154 return VAR_ERR_OUT_OF_MEMORY;
1155 }
1156 i--;
1157 }
1158 i = width - (result.end - result.begin);
1159 if (!tokenbuf_append(&result, fill->begin, i)) {
1160 tokenbuf_free(&result);
1161 return VAR_ERR_OUT_OF_MEMORY;
1162 }
1163 /* move string from temporary buffer to data buffer */
1164 tokenbuf_free(data);
1165 tokenbuf_move(&result, data);
1166 }
1167 }
1168 return VAR_OK;
1169 }
1170
1171 /* parse an integer number ("123") */
parse_integer(var_t * var,var_parse_t * ctx,const char * begin,const char * end,int * result)1172 static int parse_integer(var_t* var,
1173 var_parse_t* ctx,
1174 const char* begin,
1175 const char* end,
1176 int* result)
1177 {
1178 const char* p;
1179 int num;
1180
1181 p = begin;
1182 num = 0;
1183 while (isdigit(*p) && p != end) {
1184 num *= 10;
1185 num += (*p - '0');
1186 p++;
1187 }
1188 if (result != NULL) *result = num;
1189 return (p - begin);
1190 }
1191
1192 /* parse an operation (":x...") */
parse_operation(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * data)1193 static int parse_operation(var_t* var,
1194 var_parse_t* ctx,
1195 const char* begin,
1196 const char* end,
1197 tokenbuf_t* data)
1198 {
1199 const char* p;
1200 tokenbuf_t tmptokbuf;
1201 tokenbuf_t search, replace, flags;
1202 tokenbuf_t number1, number2;
1203 int num1, num2;
1204 int isrange;
1205 int rc;
1206 char* ptr;
1207
1208 /* initialization */
1209 tokenbuf_init(&tmptokbuf);
1210 tokenbuf_init(&search);
1211 tokenbuf_init(&replace);
1212 tokenbuf_init(&flags);
1213 tokenbuf_init(&number1);
1214 tokenbuf_init(&number2);
1215 p = begin;
1216 if (p == end) return 0;
1217
1218 /* dispatch through the first operation character */
1219 switch (tolower(*p)) {
1220 case 'l': {
1221 /* turn value to lowercase. */
1222 if (data->begin != NULL) {
1223 /* if the buffer does not live in an allocated buffer,
1224 we have to copy it before modifying the contents. */
1225 if (data->buffer_size == 0) {
1226 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1227 rc = VAR_ERR_OUT_OF_MEMORY;
1228 goto error_return;
1229 }
1230 }
1231 /* convert value */
1232 for (ptr = (char*)data->begin; ptr != data->end; ptr++)
1233 *ptr = (char)tolower((int)(*ptr));
1234 }
1235 p++;
1236 break;
1237 }
1238 case 'u': {
1239 /* turn value to uppercase. */
1240 if (data->begin != NULL) {
1241 /* if the buffer does not live in an allocated buffer,
1242 we have to copy it before modifying the contents. */
1243 if (data->buffer_size == 0) {
1244 if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
1245 rc = VAR_ERR_OUT_OF_MEMORY;
1246 goto error_return;
1247 }
1248 }
1249 /* convert value */
1250 for (ptr = (char*)data->begin; ptr != data->end; ptr++)
1251 *ptr = (char)toupper((int)(*ptr));
1252 }
1253 p++;
1254 break;
1255 }
1256 case 'o': {
1257 /* cut out substring of value. */
1258 p++;
1259 rc = parse_integer(var, ctx, p, end, &num1);
1260 if (rc == 0) {
1261 rc = VAR_ERR_MISSING_START_OFFSET;
1262 goto error_return;
1263 } else if (rc < 0)
1264 goto error_return;
1265 p += rc;
1266 if (*p == ',') {
1267 isrange = 0;
1268 p++;
1269 } else if (*p == '-') {
1270 isrange = 1;
1271 p++;
1272 } else {
1273 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
1274 goto error_return;
1275 }
1276 rc = parse_integer(var, ctx, p, end, &num2);
1277 p += rc;
1278 if (data->begin != NULL) {
1279 rc = op_offset(var, ctx, data, num1, num2, isrange);
1280 if (rc < 0) goto error_return;
1281 }
1282 break;
1283 }
1284 case '#': {
1285 /* determine length of the value */
1286 if (data->begin != NULL) {
1287 char buf[((sizeof(int) * 8) / 3)
1288 + 10]; /* sufficient size: <#bits> x log_10(2) + safety */
1289 sprintf(buf, "%d", (int)(data->end - data->begin));
1290 tokenbuf_free(data);
1291 if (!tokenbuf_assign(data, buf, strlen(buf))) {
1292 rc = VAR_ERR_OUT_OF_MEMORY;
1293 goto error_return;
1294 }
1295 }
1296 p++;
1297 break;
1298 }
1299 case '-': {
1300 /* substitute parameter if data is empty */
1301 p++;
1302 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1303 if (rc < 0) goto error_return;
1304 if (rc == 0) {
1305 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1306 goto error_return;
1307 }
1308 p += rc;
1309 if (tokenbuf_isundef(data))
1310 tokenbuf_move(&tmptokbuf, data);
1311 else if (tokenbuf_isempty(data)) {
1312 tokenbuf_free(data);
1313 tokenbuf_move(&tmptokbuf, data);
1314 }
1315 break;
1316 }
1317 case '*': {
1318 /* substitute empty string if data is not empty, parameter otherwise. */
1319 p++;
1320 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1321 if (rc < 0) goto error_return;
1322 if (rc == 0) {
1323 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1324 goto error_return;
1325 }
1326 p += rc;
1327 if (data->begin != NULL) {
1328 if (data->begin == data->end) {
1329 tokenbuf_free(data);
1330 tokenbuf_move(&tmptokbuf, data);
1331 } else {
1332 tokenbuf_free(data);
1333 data->begin = data->end = "";
1334 data->buffer_size = 0;
1335 }
1336 }
1337 break;
1338 }
1339 case '+': {
1340 /* substitute parameter if data is not empty. */
1341 p++;
1342 rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
1343 if (rc < 0) goto error_return;
1344 if (rc == 0) {
1345 rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
1346 goto error_return;
1347 }
1348 p += rc;
1349 if (data->begin != NULL && data->begin != data->end) {
1350 tokenbuf_free(data);
1351 tokenbuf_move(&tmptokbuf, data);
1352 }
1353 break;
1354 }
1355 case 's': {
1356 /* search and replace. */
1357 p++;
1358 if (*p != '/') return VAR_ERR_MALFORMATTED_REPLACE;
1359 p++;
1360 rc = parse_pattern(var, ctx, p, end);
1361 if (rc < 0) goto error_return;
1362 tokenbuf_set(&search, p, p + rc, 0);
1363 p += rc;
1364 if (*p != '/') {
1365 rc = VAR_ERR_MALFORMATTED_REPLACE;
1366 goto error_return;
1367 }
1368 p++;
1369 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1370 if (rc < 0) goto error_return;
1371 p += rc;
1372 if (*p != '/') {
1373 rc = VAR_ERR_MALFORMATTED_REPLACE;
1374 goto error_return;
1375 }
1376 p++;
1377 rc = parse_exptext(var, ctx, p, end);
1378 if (rc < 0) goto error_return;
1379 tokenbuf_set(&flags, p, p + rc, 0);
1380 p += rc;
1381 if (data->begin != NULL) {
1382 rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
1383 if (rc < 0) goto error_return;
1384 }
1385 break;
1386 }
1387 case 'y': {
1388 /* transpose characters from class A to class B. */
1389 p++;
1390 if (*p != '/') return VAR_ERR_MALFORMATTED_TRANSPOSE;
1391 p++;
1392 rc = parse_substext_or_variable(var, ctx, p, end, &search);
1393 if (rc < 0) goto error_return;
1394 p += rc;
1395 if (*p != '/') {
1396 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1397 goto error_return;
1398 }
1399 p++;
1400 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1401 if (rc < 0) goto error_return;
1402 p += rc;
1403 if (*p != '/') {
1404 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
1405 goto error_return;
1406 } else
1407 p++;
1408 if (data->begin) {
1409 rc = op_transpose(var, ctx, data, &search, &replace);
1410 if (rc < 0) goto error_return;
1411 }
1412 break;
1413 }
1414 case 'p': {
1415 /* padding. */
1416 p++;
1417 if (*p != '/') return VAR_ERR_MALFORMATTED_PADDING;
1418 p++;
1419 rc = parse_integer(var, ctx, p, end, &num1);
1420 if (rc == 0) {
1421 rc = VAR_ERR_MISSING_PADDING_WIDTH;
1422 goto error_return;
1423 }
1424 p += rc;
1425 if (*p != '/') {
1426 rc = VAR_ERR_MALFORMATTED_PADDING;
1427 goto error_return;
1428 }
1429 p++;
1430 rc = parse_substext_or_variable(var, ctx, p, end, &replace);
1431 if (rc < 0) goto error_return;
1432 p += rc;
1433 if (*p != '/') {
1434 rc = VAR_ERR_MALFORMATTED_PADDING;
1435 goto error_return;
1436 }
1437 p++;
1438 if (*p != 'l' && *p != 'c' && *p != 'r') {
1439 rc = VAR_ERR_MALFORMATTED_PADDING;
1440 goto error_return;
1441 }
1442 p++;
1443 if (data->begin) {
1444 rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
1445 if (rc < 0) goto error_return;
1446 }
1447 break;
1448 }
1449 case '%': {
1450 /* operation callback function */
1451 const char* op_ptr;
1452 int op_len;
1453 const char* arg_ptr;
1454 int arg_len;
1455 const char* val_ptr;
1456 int val_len;
1457 const char* out_ptr;
1458 int out_len;
1459 int out_size;
1460 tokenbuf_t args;
1461
1462 p++;
1463 rc = parse_name(var, ctx, p, end);
1464 if (rc < 0) goto error_return;
1465 op_ptr = p;
1466 op_len = rc;
1467 p += rc;
1468 if (*p == '(') {
1469 p++;
1470 tokenbuf_init(&args);
1471 rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
1472 if (rc < 0) goto error_return;
1473 p += rc;
1474 arg_ptr = args.begin;
1475 arg_len = args.end - args.begin;
1476 if (*p != ')') {
1477 rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
1478 goto error_return;
1479 }
1480 p++;
1481 } else {
1482 arg_ptr = NULL;
1483 arg_len = 0;
1484 }
1485 val_ptr = data->begin;
1486 val_len = data->end - data->begin;
1487
1488 if (data->begin != NULL && var->cb_operation_fct != NULL) {
1489 /* call operation callback function */
1490 rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx, op_ptr,
1491 op_len, arg_ptr, arg_len, val_ptr,
1492 val_len, &out_ptr, &out_len, &out_size);
1493 if (rc < 0) {
1494 if (arg_ptr != NULL) free((void*)arg_ptr);
1495 goto error_return;
1496 }
1497 tokenbuf_free(data);
1498 tokenbuf_set(data, out_ptr, out_ptr + out_len, out_size);
1499 }
1500 if (arg_ptr != NULL) free((void*)arg_ptr);
1501 break;
1502 }
1503 default:
1504 return VAR_ERR_UNKNOWN_COMMAND_CHAR;
1505 }
1506
1507 /* return successfully */
1508 tokenbuf_free(&tmptokbuf);
1509 tokenbuf_free(&search);
1510 tokenbuf_free(&replace);
1511 tokenbuf_free(&flags);
1512 tokenbuf_free(&number1);
1513 tokenbuf_free(&number2);
1514 return (p - begin);
1515
1516 /* return with an error */
1517 error_return:
1518 tokenbuf_free(data);
1519 tokenbuf_free(&tmptokbuf);
1520 tokenbuf_free(&search);
1521 tokenbuf_free(&replace);
1522 tokenbuf_free(&flags);
1523 tokenbuf_free(&number1);
1524 tokenbuf_free(&number2);
1525 return rc;
1526 }
1527
1528 /* parse numerical expression operand */
parse_numexp_operand(var_t * var,var_parse_t * ctx,const char * begin,const char * end,int * result,int * failed)1529 static int parse_numexp_operand(var_t* var,
1530 var_parse_t* ctx,
1531 const char* begin,
1532 const char* end,
1533 int* result,
1534 int* failed)
1535 {
1536 const char* p;
1537 tokenbuf_t tmp;
1538 int rc;
1539 var_parse_t myctx;
1540
1541 /* initialization */
1542 p = begin;
1543 tokenbuf_init(&tmp);
1544 if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1545
1546 /* parse opening numerical expression */
1547 if (*p == '(') {
1548 /* parse inner numerical expression */
1549 rc = parse_numexp(var, ctx, ++p, end, result, failed);
1550 if (rc < 0) return rc;
1551 p += rc;
1552 if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1553 /* parse closing parenthesis */
1554 if (*p != ')') return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
1555 p++;
1556 }
1557 /* parse contained variable */
1558 else if (*p == var->syntax.delim_init) {
1559 /* parse variable with forced expansion */
1560 ctx = var_parse_push(ctx, &myctx);
1561 ctx->force_expand = 1;
1562 rc = ParseVariable(var, ctx, p, end, &tmp);
1563 ctx = var_parse_pop(ctx);
1564
1565 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1566 *failed = 1;
1567 /* parse variable without forced expansion */
1568 ctx = var_parse_push(ctx, &myctx);
1569 ctx->force_expand = 0;
1570 rc = ParseVariable(var, ctx, p, end, &tmp);
1571 ctx = var_parse_pop(ctx);
1572 if (rc < 0) return rc;
1573 p += rc;
1574 *result = 0;
1575 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1576 } else if (rc < 0) {
1577 return rc;
1578 } else {
1579 p += rc;
1580 /* parse remaining numerical expression */
1581 rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
1582 tokenbuf_free(&tmp);
1583 if (rc < 0) return rc;
1584 }
1585 }
1586 /* parse relative index mark ("#") */
1587 else if (var->syntax.index_mark != EOS && *p == var->syntax.index_mark) {
1588 p++;
1589 *result = ctx->index_this;
1590 if (ctx->rel_lookup_flag) ctx->rel_lookup_cnt++;
1591 }
1592 /* parse plain integer number */
1593 else if (isdigit(*p)) {
1594 rc = parse_integer(var, ctx, p, end, result);
1595 p += rc;
1596 }
1597 /* parse signed positive integer number */
1598 else if (*p == '+') {
1599 if ((end - p) > 1 && isdigit(p[1])) {
1600 p++;
1601 rc = parse_integer(var, ctx, p, end, result);
1602 p += rc;
1603 } else
1604 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1605 }
1606 /* parse signed negative integer number */
1607 else if (*p == '-') {
1608 if (end - p > 1 && isdigit(p[1])) {
1609 p++;
1610 rc = parse_integer(var, ctx, p, end, result);
1611 *result = -(*result);
1612 p += rc;
1613 } else
1614 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1615 }
1616 /* else we failed to parse anything reasonable */
1617 else
1618 return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1619
1620 return (p - begin);
1621 }
1622
1623 /* parse numerical expression ("x+y") */
parse_numexp(var_t * var,var_parse_t * ctx,const char * begin,const char * end,int * result,int * failed)1624 static int parse_numexp(var_t* var,
1625 var_parse_t* ctx,
1626 const char* begin,
1627 const char* end,
1628 int* result,
1629 int* failed)
1630 {
1631 const char* p;
1632 char op;
1633 int right;
1634 int rc;
1635
1636 /* initialization */
1637 p = begin;
1638 if (p == end) return VAR_ERR_INCOMPLETE_INDEX_SPEC;
1639
1640 /* parse left numerical operand */
1641 rc = parse_numexp_operand(var, ctx, p, end, result, failed);
1642 if (rc < 0) return rc;
1643 p += rc;
1644
1645 /* parse numerical operator */
1646 while (p != end) {
1647 if (*p == '+' || *p == '-') {
1648 op = *p++;
1649 /* recursively parse right operand (light binding) */
1650 rc = parse_numexp(var, ctx, p, end, &right, failed);
1651 if (rc < 0) return rc;
1652 p += rc;
1653 if (op == '+')
1654 *result = (*result + right);
1655 else
1656 *result = (*result - right);
1657 } else if (*p == '*' || *p == '/' || *p == '%') {
1658 op = *p++;
1659 /* recursively parse right operand (string binding) */
1660 rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
1661 if (rc < 0) return rc;
1662 p += rc;
1663 if (op == '*')
1664 *result = (*result * right);
1665 else if (op == '/') {
1666 if (right == 0) {
1667 if (*failed)
1668 *result = 0;
1669 else
1670 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1671 } else
1672 *result = (*result / right);
1673 } else if (op == '%') {
1674 if (right == 0) {
1675 if (*failed)
1676 *result = 0;
1677 else
1678 return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
1679 } else
1680 *result = (*result % right);
1681 }
1682 } else
1683 break;
1684 }
1685
1686 /* return amount of parsed input */
1687 return (p - begin);
1688 }
1689
1690 /* parse variable name ("abc") */
parse_name(var_t * var,var_parse_t * ctx,const char * begin,const char * end)1691 static int parse_name(var_t* var,
1692 var_parse_t* ctx,
1693 const char* begin,
1694 const char* end)
1695 {
1696 const char* p;
1697
1698 /* parse as long as name class characters are found */
1699 for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
1700 ;
1701 return (p - begin);
1702 }
1703
1704 /* lookup a variable value through the callback function */
lookup_value(var_t * var,var_parse_t * ctx,const char * var_ptr,int var_len,int var_inc,int var_idx,const char ** val_ptr,int * val_len,int * val_size)1705 static int lookup_value(var_t* var,
1706 var_parse_t* ctx,
1707 const char* var_ptr,
1708 int var_len,
1709 int var_inc,
1710 int var_idx,
1711 const char** val_ptr,
1712 int* val_len,
1713 int* val_size)
1714 {
1715 char buf[1];
1716 int rc;
1717
1718 /* pass through to original callback */
1719 rc = (*var->cb_value_fct)(var, var->cb_value_ctx, var_ptr, var_len, var_inc,
1720 var_idx, val_ptr, val_len, val_size);
1721
1722 /* convert undefined variable into empty variable if relative
1723 lookups are counted. This is the case inside an active loop
1724 construct if no limits are given. There the parse_input()
1725 has to proceed until all variables have undefined values.
1726 This trick here allows it to determine this case. */
1727 if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
1728 ctx->rel_lookup_cnt--;
1729 buf[0] = EOS;
1730 *val_ptr = strdup(buf);
1731 *val_len = 0;
1732 *val_size = 0;
1733 return VAR_OK;
1734 }
1735
1736 return rc;
1737 }
1738
1739 /* parse complex variable construct ("${name...}") */
parse_variable_complex(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * result)1740 static int parse_variable_complex(var_t* var,
1741 var_parse_t* ctx,
1742 const char* begin,
1743 const char* end,
1744 tokenbuf_t* result)
1745 {
1746 const char* p;
1747 const char* data;
1748 int len, buffer_size;
1749 int failed = 0;
1750 int rc;
1751 int idx = 0;
1752 int inc;
1753 tokenbuf_t name;
1754 tokenbuf_t tmp;
1755
1756 /* initializations */
1757 p = begin;
1758 tokenbuf_init(&name);
1759 tokenbuf_init(&tmp);
1760 tokenbuf_init(result);
1761
1762 /* parse open delimiter */
1763 if (p == end || *p != var->syntax.delim_open) return 0;
1764 p++;
1765 if (p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1766
1767 /* parse name of variable to expand. The name may consist of an
1768 arbitrary number of variable name character and contained variable
1769 constructs. */
1770 do {
1771 /* parse a variable name */
1772 rc = parse_name(var, ctx, p, end);
1773 if (rc < 0) goto error_return;
1774 if (rc > 0) {
1775 if (!tokenbuf_append(&name, p, rc)) {
1776 rc = VAR_ERR_OUT_OF_MEMORY;
1777 goto error_return;
1778 }
1779 p += rc;
1780 }
1781
1782 /* parse an (embedded) variable */
1783 rc = ParseVariable(var, ctx, p, end, &tmp);
1784 if (rc < 0) goto error_return;
1785 if (rc > 0) {
1786 if (!tokenbuf_merge(&name, &tmp)) {
1787 rc = VAR_ERR_OUT_OF_MEMORY;
1788 goto error_return;
1789 }
1790 p += rc;
1791 }
1792 tokenbuf_free(&tmp); /* KES 11/9/2003 */
1793 } while (rc > 0);
1794
1795 /* we must have the complete expanded variable name now,
1796 so make sure we really do. */
1797 if (name.begin == name.end) {
1798 if (ctx->force_expand) {
1799 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1800 goto error_return;
1801 } else {
1802 /* If no force_expand is requested, we have to back-off.
1803 We're not sure whether our approach here is 100% correct,
1804 because it _could_ have side-effects according to Peter
1805 Simons, but as far as we know and tried it, it is
1806 correct. But be warned -- RSE */
1807 tokenbuf_set(result, begin - 1, p, 0);
1808 goto goahead;
1809 }
1810 }
1811
1812 /* parse an optional index specification */
1813 if (var->syntax.index_open != EOS && *p == var->syntax.index_open) {
1814 p++;
1815 rc = parse_numexp(var, ctx, p, end, &idx, &failed);
1816 if (rc < 0) goto error_return;
1817 if (rc == 0) {
1818 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1819 goto error_return;
1820 }
1821 p += rc;
1822 if (p == end) {
1823 rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
1824 goto error_return;
1825 }
1826 if (*p != var->syntax.index_close) {
1827 rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
1828 goto error_return;
1829 }
1830 p++;
1831 }
1832
1833 /* parse end of variable construct or start of post-operations */
1834 if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
1835 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1836 goto error_return;
1837 }
1838 if ((inc = (*p++ == '+'))) { p++; /* skip the + */ }
1839
1840 /* lookup the variable value now */
1841 if (failed) {
1842 tokenbuf_set(result, begin - 1, p, 0);
1843 } else {
1844 rc = lookup_value(var, ctx, name.begin, name.end - name.begin, inc, idx,
1845 &data, &len, &buffer_size);
1846 if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
1847 tokenbuf_init(result); /* delayed handling of undefined variable */
1848 } else if (rc < 0) {
1849 goto error_return;
1850 } else {
1851 /* the preliminary result is the raw value of the variable.
1852 This may be modified by the operations that may follow. */
1853 tokenbuf_set(result, data, data + len, buffer_size);
1854 }
1855 }
1856
1857 /* parse optional post-operations */
1858 goahead:
1859 if (p[-1] == ':') {
1860 tokenbuf_free(&tmp);
1861 tokenbuf_init(&tmp);
1862 p--;
1863 while (p != end && *p == ':') {
1864 p++;
1865 if (!failed)
1866 rc = parse_operation(var, ctx, p, end, result);
1867 else
1868 rc = parse_operation(var, ctx, p, end, &tmp);
1869 if (rc < 0) goto error_return;
1870 p += rc;
1871 if (failed) result->end += rc;
1872 }
1873 if (p == end || *p != var->syntax.delim_close) {
1874 rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1875 goto error_return;
1876 }
1877 p++;
1878 if (failed) result->end++;
1879 } else if (p[-1] == '+') {
1880 p++;
1881 }
1882
1883 /* lazy handling of undefined variable */
1884 if (!failed && tokenbuf_isundef(result)) {
1885 if (ctx->force_expand) {
1886 rc = VAR_ERR_UNDEFINED_VARIABLE;
1887 goto error_return;
1888 } else {
1889 tokenbuf_set(result, begin - 1, p, 0);
1890 }
1891 }
1892
1893 /* return successfully */
1894 tokenbuf_free(&name);
1895 tokenbuf_free(&tmp);
1896 return (p - begin);
1897
1898 /* return with an error */
1899 error_return:
1900 tokenbuf_free(&name);
1901 tokenbuf_free(&tmp);
1902 tokenbuf_free(result);
1903 return rc;
1904 }
1905
1906 /* parse variable construct ("$name" or "${name...}") */
ParseVariable(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * result)1907 static int ParseVariable(var_t* var,
1908 var_parse_t* ctx,
1909 const char* begin,
1910 const char* end,
1911 tokenbuf_t* result)
1912 {
1913 const char* p;
1914 const char* data;
1915 int len, buffer_size;
1916 int rc, rc2;
1917 int inc;
1918
1919 /* initialization */
1920 p = begin;
1921 tokenbuf_init(result);
1922
1923 /* parse init delimiter */
1924 if (p == end || *p != var->syntax.delim_init) return 0;
1925 p++;
1926 if (p == end) return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
1927
1928 /* parse a simple variable name.
1929 (if this fails, we're try to parse a complex variable construct) */
1930 rc = parse_name(var, ctx, p, end);
1931 if (rc < 0) return rc;
1932 if (rc > 0) {
1933 inc = (p[rc] == '+');
1934 rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
1935 if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
1936 tokenbuf_set(result, begin, begin + 1 + rc, 0);
1937 return (1 + rc);
1938 }
1939 if (rc2 < 0) return rc2;
1940 tokenbuf_set(result, data, data + len, buffer_size);
1941 return (1 + rc);
1942 }
1943
1944 /* parse a complex variable construct (else case) */
1945 rc = parse_variable_complex(var, ctx, p, end, result);
1946 if (rc > 0) rc++;
1947 return rc;
1948 }
1949
1950 /* parse loop construct limits ("[...]{b,s,e}") */
parse_looplimits(var_t * var,var_parse_t * ctx,const char * begin,const char * end,int * start,int * step,int * stop,int * open_stop)1951 static var_rc_t parse_looplimits(var_t* var,
1952 var_parse_t* ctx,
1953 const char* begin,
1954 const char* end,
1955 int* start,
1956 int* step,
1957 int* stop,
1958 int* open_stop)
1959 {
1960 const char* p;
1961 int rc;
1962 int failed;
1963
1964 /* initialization */
1965 p = begin;
1966
1967 /* we are happy if nothing is to left to parse */
1968 if (p == end) return VAR_OK;
1969
1970 /* parse start delimiter */
1971 if (*p != var->syntax.delim_open) return VAR_OK;
1972 p++;
1973
1974 /* parse loop start value */
1975 failed = 0;
1976 rc = parse_numexp(var, ctx, p, end, start, &failed);
1977 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
1978 *start = 0; /* use default */
1979 else if (rc < 0)
1980 return (var_rc_t)rc;
1981 else
1982 p += rc;
1983 if (failed) return VAR_ERR_UNDEFINED_VARIABLE;
1984
1985 /* parse separator */
1986 if (*p != ',') return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
1987 p++;
1988
1989 /* parse loop step value */
1990 failed = 0;
1991 rc = parse_numexp(var, ctx, p, end, step, &failed);
1992 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
1993 *step = 1; /* use default */
1994 else if (rc < 0)
1995 return (var_rc_t)rc;
1996 else
1997 p += rc;
1998 if (failed) return VAR_ERR_UNDEFINED_VARIABLE;
1999
2000 /* parse separator */
2001 if (*p != ',') {
2002 /* if not found, parse end delimiter */
2003 if (*p != var->syntax.delim_close)
2004 return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2005 p++;
2006
2007 /* shift step value to stop value */
2008 *stop = *step;
2009 *step = 1;
2010
2011 /* determine whether loop end is open */
2012 if (rc > 0)
2013 *open_stop = 0;
2014 else
2015 *open_stop = 1;
2016 return (var_rc_t)(p - begin);
2017 }
2018 p++;
2019
2020 /* parse loop stop value */
2021 failed = 0;
2022 rc = parse_numexp(var, ctx, p, end, stop, &failed);
2023 if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
2024 *stop = 0; /* use default */
2025 *open_stop = 1;
2026 } else if (rc < 0)
2027 return (var_rc_t)rc;
2028 else {
2029 *open_stop = 0;
2030 p += rc;
2031 }
2032 if (failed) return VAR_ERR_UNDEFINED_VARIABLE;
2033
2034 /* parse end delimiter */
2035 if (*p != var->syntax.delim_close) return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
2036 p++;
2037
2038 /* return amount of parsed input */
2039 return (var_rc_t)(p - begin);
2040 }
2041
2042 /* parse plain text */
parse_text(var_t * var,var_parse_t * ctx,const char * begin,const char * end)2043 static int parse_text(var_t* var,
2044 var_parse_t* ctx,
2045 const char* begin,
2046 const char* end)
2047 {
2048 const char* p;
2049
2050 /* parse until delim_init (variable construct)
2051 or index_open (loop construct) is found */
2052 for (p = begin; p != end; p++) {
2053 if (*p == var->syntax.escape) {
2054 p++; /* skip next character */
2055 if (p == end) return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
2056 } else if (*p == var->syntax.delim_init)
2057 break;
2058 else if (var->syntax.index_open != EOS
2059 && (*p == var->syntax.index_open || *p == var->syntax.index_close))
2060 break;
2061 }
2062 return (p - begin);
2063 }
2064
2065 /* expand input in general */
parse_input(var_t * var,var_parse_t * ctx,const char * begin,const char * end,tokenbuf_t * output,int recursion_level)2066 static var_rc_t parse_input(var_t* var,
2067 var_parse_t* ctx,
2068 const char* begin,
2069 const char* end,
2070 tokenbuf_t* output,
2071 int recursion_level)
2072 {
2073 const char* p;
2074 int rc, rc2;
2075 int start, step, stop, open_stop;
2076 int i;
2077 int output_backup;
2078 int rel_lookup_cnt;
2079 int loop_limit_length;
2080 var_parse_t myctx;
2081
2082 /* initialization */
2083 p = begin;
2084
2085 do {
2086 /* try to parse a loop construct */
2087 if (p != end && var->syntax.index_open != EOS
2088 && *p == var->syntax.index_open) {
2089 p++;
2090
2091 /* loop preparation */
2092 loop_limit_length = -1;
2093 rel_lookup_cnt = ctx->rel_lookup_cnt;
2094 open_stop = 1;
2095 rc = 0;
2096 start = 0;
2097 step = 1;
2098 stop = 0;
2099 output_backup = 0;
2100
2101 /* iterate over loop construct, either as long as there is
2102 (still) nothing known about the limit, or there is an open
2103 (=unknown) limit stop and there are still defined variables
2104 or there is a stop limit known and it is still not reached */
2105 re_loop:
2106 for (i = start;
2107 ((open_stop
2108 && (loop_limit_length < 0 || rel_lookup_cnt > ctx->rel_lookup_cnt))
2109 || (!open_stop && i <= stop));
2110 i += step) {
2111 /* remember current output end for restoring */
2112 output_backup = (output->end - output->begin);
2113
2114 /* open temporary context for recursion */
2115 ctx = var_parse_push(ctx, &myctx);
2116 ctx->force_expand = 1;
2117 ctx->rel_lookup_flag = 1;
2118 ctx->index_this = i;
2119
2120 /* recursive parse input through ourself */
2121 rc = parse_input(var, ctx, p, end, output, recursion_level + 1);
2122
2123 /* retrieve info and close temporary context */
2124 rel_lookup_cnt = ctx->rel_lookup_cnt;
2125 ctx = var_parse_pop(ctx);
2126
2127 /* error handling */
2128 if (rc < 0) goto error_return;
2129
2130 /* make sure the loop construct is closed */
2131 if (p[rc] != var->syntax.index_close) {
2132 rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
2133 goto error_return;
2134 }
2135
2136 /* try to parse loop construct limit specification */
2137 if (loop_limit_length < 0) {
2138 rc2 = parse_looplimits(var, ctx, p + rc + 1, end, &start, &step,
2139 &stop, &open_stop);
2140 if (rc2 < 0)
2141 goto error_return;
2142 else if (rc2 == 0)
2143 loop_limit_length = 0;
2144 else if (rc2 > 0) {
2145 loop_limit_length = rc2;
2146 /* restart loop from scratch */
2147 output->end = (output->begin + output_backup);
2148 goto re_loop;
2149 }
2150 }
2151 }
2152
2153 /* if stop value is open, restore to the output end
2154 because the last iteration was just to determine the loop
2155 termination and its result has to be discarded */
2156 if (open_stop) output->end = (output->begin + output_backup);
2157
2158 /* skip parsed loop construct */
2159 p += rc;
2160 p++;
2161 p += loop_limit_length;
2162
2163 continue;
2164 }
2165
2166 /* try to parse plain text */
2167 rc = parse_text(var, ctx, p, end);
2168 if (rc > 0) {
2169 if (!tokenbuf_append(output, p, rc)) {
2170 rc = VAR_ERR_OUT_OF_MEMORY;
2171 goto error_return;
2172 }
2173 p += rc;
2174 continue;
2175 } else if (rc < 0)
2176 goto error_return;
2177
2178 /* try to parse a variable construct */
2179 tokenbuf_t result;
2180 tokenbuf_init(&result);
2181 rc = ParseVariable(var, ctx, p, end, &result);
2182 if (rc > 0) {
2183 if (!tokenbuf_merge(output, &result)) {
2184 tokenbuf_free(&result);
2185 rc = VAR_ERR_OUT_OF_MEMORY;
2186 goto error_return;
2187 }
2188 tokenbuf_free(&result);
2189 p += rc;
2190 continue;
2191 }
2192 tokenbuf_free(&result);
2193 if (rc < 0) goto error_return;
2194
2195 } while (p != end && rc > 0);
2196
2197 /* We do not know whether this really could happen, but because we
2198 are paranoid, report an error at the outer most parsing level if
2199 there is still any input. Because this would mean that we are no
2200 longer able to parse the remaining input as a loop construct, a
2201 text or a variable construct. This would be very strange, but
2202 could perhaps happen in case of configuration errors!?... */
2203 if (recursion_level == 0 && p != end) {
2204 rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
2205 goto error_return;
2206 }
2207
2208 /* return amount of parsed text */
2209 return (var_rc_t)(p - begin);
2210
2211 /* return with an error where as a special case the output begin is
2212 set to the input begin and the output end to the last input parsing
2213 position. */
2214 error_return:
2215 tokenbuf_free(output);
2216 tokenbuf_set(output, begin, p, 0);
2217 return (var_rc_t)rc;
2218 }
2219
2220 /*
2221 **
2222 ** ==== APPLICATION PROGRAMMING INTERFACE (API) ====
2223 **
2224 */
2225
2226 /* create variable expansion context */
var_create(var_t ** pvar)2227 var_rc_t var_create(var_t** pvar)
2228 {
2229 var_t* var;
2230
2231 if (pvar == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2232 if ((var = (var_t*)malloc(sizeof(var_t))) == NULL)
2233 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2234 memset(var, 0, sizeof(var_t));
2235 var_config(var, var_config_t::VAR_CONFIG_SYNTAX, &var_syntax_default);
2236 *pvar = var;
2237 return VAR_OK;
2238 }
2239
2240 /* destroy variable expansion context */
var_destroy(var_t * var)2241 var_rc_t var_destroy(var_t* var)
2242 {
2243 if (var == NULL) return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2244 free(var);
2245 return VAR_OK;
2246 }
2247
2248 /* configure variable expansion context */
var_config(var_t * var,var_config_t mode,...)2249 var_rc_t var_config(var_t* var, var_config_t mode, ...)
2250 {
2251 va_list ap;
2252 var_rc_t rc = VAR_OK;
2253
2254 if (var == NULL) { return VAR_RC(VAR_ERR_INVALID_ARGUMENT); }
2255
2256 va_start(ap, mode);
2257 switch (mode) {
2258 case var_config_t::VAR_CONFIG_SYNTAX: {
2259 var_syntax_t* s;
2260 s = (var_syntax_t*)va_arg(ap, void*);
2261 if (s == NULL) {
2262 rc = VAR_ERR_INVALID_ARGUMENT;
2263 goto bail_out;
2264 }
2265 var->syntax.escape = s->escape;
2266 var->syntax.delim_init = s->delim_init;
2267 var->syntax.delim_open = s->delim_open;
2268 var->syntax.delim_close = s->delim_close;
2269 var->syntax.index_open = s->index_open;
2270 var->syntax.index_close = s->index_close;
2271 var->syntax.index_mark = s->index_mark;
2272 var->syntax.name_chars = NULL; /* unused internally */
2273 if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass))
2274 != VAR_OK) {
2275 goto bail_out;
2276 }
2277 if (var->syntax_nameclass[(int)var->syntax.delim_init]
2278 || var->syntax_nameclass[(int)var->syntax.delim_open]
2279 || var->syntax_nameclass[(int)var->syntax.delim_close]
2280 || var->syntax_nameclass[(int)var->syntax.escape]) {
2281 rc = VAR_ERR_INVALID_CONFIGURATION;
2282 goto bail_out;
2283 }
2284 break;
2285 }
2286 case var_config_t::VAR_CONFIG_CB_VALUE: {
2287 var_cb_value_t fct;
2288 void* ctx;
2289
2290 fct = (var_cb_value_t)va_arg(ap, void*);
2291 ctx = (void*)va_arg(ap, void*);
2292 var->cb_value_fct = fct;
2293 var->cb_value_ctx = ctx;
2294 break;
2295 }
2296 case var_config_t::VAR_CONFIG_CB_OPERATION: {
2297 var_cb_operation_t fct;
2298 void* ctx;
2299
2300 fct = (var_cb_operation_t)va_arg(ap, void*);
2301 ctx = (void*)va_arg(ap, void*);
2302 var->cb_operation_fct = fct;
2303 var->cb_operation_ctx = ctx;
2304 break;
2305 }
2306 default:
2307 rc = VAR_ERR_INVALID_ARGUMENT;
2308 goto bail_out;
2309 }
2310
2311 bail_out:
2312 va_end(ap);
2313 return VAR_RC(rc);
2314 }
2315
2316 /* perform unescape operation on a buffer */
var_unescape(var_t * var,const char * src,int srclen,char * dst,int dstlen,int all)2317 var_rc_t var_unescape(var_t* var,
2318 const char* src,
2319 int srclen,
2320 char* dst,
2321 int dstlen,
2322 int all)
2323 {
2324 const char* end;
2325 var_rc_t rc;
2326
2327 if (var == NULL || src == NULL || dst == NULL)
2328 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2329 end = src + srclen;
2330 while (src < end) {
2331 if (*src == '\\') {
2332 if (++src == end) return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
2333 switch (*src) {
2334 case '\\':
2335 if (!all) { *dst++ = '\\'; }
2336 *dst++ = '\\';
2337 break;
2338 case 'n':
2339 *dst++ = '\n';
2340 break;
2341 case 't':
2342 *dst++ = '\t';
2343 break;
2344 case 'r':
2345 *dst++ = '\r';
2346 break;
2347 case 'x':
2348 ++src;
2349 if ((rc = expand_hex(&src, &dst, end)) != VAR_OK) return VAR_RC(rc);
2350 break;
2351 case '0':
2352 case '1':
2353 case '2':
2354 case '3':
2355 case '4':
2356 case '5':
2357 case '6':
2358 case '7':
2359 case '8':
2360 case '9':
2361 if (end - src >= 3 && isdigit((int)src[1]) && isdigit((int)src[2])) {
2362 if ((rc = expand_octal(&src, &dst, end)) != 0) return VAR_RC(rc);
2363 break;
2364 }
2365 default:
2366 if (!all) { *dst++ = '\\'; }
2367 *dst++ = *src;
2368 }
2369 ++src;
2370 } else
2371 *dst++ = *src++;
2372 }
2373 *dst = EOS;
2374 return VAR_OK;
2375 }
2376
2377 /* perform expand operation on a buffer */
var_expand(var_t * var,const char * src_ptr,int src_len,char ** dst_ptr,int * dst_len,int force_expand)2378 var_rc_t var_expand(var_t* var,
2379 const char* src_ptr,
2380 int src_len,
2381 char** dst_ptr,
2382 int* dst_len,
2383 int force_expand)
2384 {
2385 var_parse_t ctx;
2386 tokenbuf_t output;
2387 var_rc_t rc;
2388
2389 /* argument sanity checks */
2390 if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
2391 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2392
2393 /* prepare internal expansion context */
2394 ctx.lower = NULL;
2395 ctx.force_expand = force_expand;
2396 ctx.rel_lookup_flag = 0;
2397 ctx.rel_lookup_cnt = 0;
2398 ctx.index_this = 0;
2399
2400 /* start the parsing */
2401 tokenbuf_init(&output);
2402 rc = parse_input(var, &ctx, src_ptr, src_ptr + src_len, &output, 0);
2403
2404 /* post-processing */
2405 if (rc >= 0) {
2406 /* always EOS-Terminate output for convinience reasons
2407 but do not count the EOS-terminator in the length */
2408 if (!tokenbuf_append(&output, "\0", 1)) {
2409 tokenbuf_free(&output);
2410 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2411 }
2412 output.end--;
2413
2414 /* provide result */
2415 *dst_ptr = (char*)output.begin;
2416 if (dst_len != NULL) *dst_len = (output.end - output.begin);
2417 rc = VAR_OK;
2418 } else {
2419 /* provide result */
2420 if (dst_len != NULL) *dst_len = (output.end - output.begin);
2421 }
2422
2423 return VAR_RC(rc);
2424 }
2425
2426 /* format and expand a string */
var_formatv(var_t * var,char ** dst_ptr,int force_expand,const char * fmt,va_list ap)2427 var_rc_t var_formatv(var_t* var,
2428 char** dst_ptr,
2429 int force_expand,
2430 const char* fmt,
2431 va_list ap)
2432 {
2433 var_rc_t rc;
2434 char* cpBuf;
2435 int nBuf = 5000;
2436
2437 /* argument sanity checks */
2438 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2439 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2440
2441 /* perform formatting */
2442 if ((cpBuf = (char*)malloc(nBuf + 1)) == NULL)
2443 return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
2444 nBuf = var_mvsnprintf(cpBuf, nBuf + 1, fmt, ap);
2445 if (nBuf == -1) {
2446 free(cpBuf);
2447 return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
2448 }
2449
2450 /* perform expansion */
2451 if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand))
2452 != VAR_OK) {
2453 free(cpBuf);
2454 return VAR_RC(rc);
2455 }
2456
2457 /* cleanup */
2458 free(cpBuf);
2459
2460 return VAR_OK;
2461 }
2462
2463 /* format and expand a string */
var_format(var_t * var,char ** dst_ptr,int force_expand,const char * fmt,...)2464 var_rc_t var_format(var_t* var,
2465 char** dst_ptr,
2466 int force_expand,
2467 const char* fmt,
2468 ...)
2469 {
2470 var_rc_t rc;
2471 va_list ap;
2472
2473 /* argument sanity checks */
2474 if (var == NULL || dst_ptr == NULL || fmt == NULL)
2475 return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
2476
2477 va_start(ap, fmt);
2478 rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
2479 va_end(ap);
2480
2481 return VAR_RC(rc);
2482 }
2483
2484 /* var_rc_t to string mapping table */
2485 static const char* var_errors[] = {
2486 _("everything ok"), /* VAR_OK = 0 */
2487 _("incomplete named character"), /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
2488 _("incomplete hexadecimal value"), /* VAR_ERR_INCOMPLETE_HEX */
2489 _("invalid hexadecimal value"), /* VAR_ERR_INVALID_HEX */
2490 _("octal value too large"), /* VAR_ERR_OCTAL_TOO_LARGE */
2491 _("invalid octal value"), /* VAR_ERR_INVALID_OCTAL */
2492 _("incomplete octal value"), /* VAR_ERR_INCOMPLETE_OCTAL */
2493 _("incomplete grouped hexadecimal value"), /* VAR_ERR_INCOMPLETE_GROUPED_HEX
2494 */
2495 _("incorrect character class specification"), /* VAR_ERR_INCORRECT_CLASS_SPEC
2496 */
2497 _("invalid expansion configuration"), /* VAR_ERR_INVALID_CONFIGURATION */
2498 _("out of memory"), /* VAR_ERR_OUT_OF_MEMORY */
2499 _("incomplete variable specification"), /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC
2500 */
2501 _("undefined variable"), /* VAR_ERR_UNDEFINED_VARIABLE */
2502 _("input is neither text nor variable"), /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE
2503 */
2504 _("unknown command character in variable"), /* VAR_ERR_UNKNOWN_COMMAND_CHAR
2505 */
2506 _("malformatted search and replace operation"), /* VAR_ERR_MALFORMATTED_REPLACE
2507 */
2508 _("unknown flag in search and replace operation"), /* VAR_ERR_UNKNOWN_REPLACE_FLAG
2509 */
2510 _("invalid regex in search and replace operation"), /* VAR_ERR_INVALID_REGEX_IN_REPLACE
2511 */
2512 _("missing parameter in command"), /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND
2513 */
2514 _("empty search string in search and replace operation"), /* VAR_ERR_EMPTY_SEARCH_STRING
2515 */
2516 _("start offset missing in cut operation"), /* VAR_ERR_MISSING_START_OFFSET
2517 */
2518 _("offsets in cut operation delimited by unknown character"), /* VAR_ERR_INVALID_OFFSET_DELIMITER
2519 */
2520 _("range out of bounds in cut operation"), /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
2521 _("offset out of bounds in cut operation"), /* VAR_ERR_OFFSET_OUT_OF_BOUNDS
2522 */
2523 _("logic error in cut operation"), /* VAR_ERR_OFFSET_LOGIC */
2524 _("malformatted transpose operation"), /* VAR_ERR_MALFORMATTED_TRANSPOSE */
2525 _("source and target class mismatch in transpose operation"), /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH
2526 */
2527 _("empty character class in transpose operation"), /* VAR_ERR_EMPTY_TRANSPOSE_CLASS
2528 */
2529 _("incorrect character class in transpose operation"), /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC
2530 */
2531 _("malformatted padding operation"), /* VAR_ERR_MALFORMATTED_PADDING */
2532 _("width parameter missing in padding operation"), /* VAR_ERR_MISSING_PADDING_WIDTH
2533 */
2534 _("fill string missing in padding operation"), /* VAR_ERR_EMPTY_PADDING_FILL_STRING
2535 */
2536 _("unknown quoted pair in search and replace operation"), /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE
2537 */
2538 _("sub-matching reference out of range"), /* VAR_ERR_SUBMATCH_OUT_OF_RANGE
2539 */
2540 _("invalid argument"), /* VAR_ERR_INVALID_ARGUMENT */
2541 _("incomplete quoted pair"), /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
2542 _("lookup function does not support variable arrays"), /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED
2543 */
2544 _("index of array variable contains an invalid character"), /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC
2545 */
2546 _("index of array variable is incomplete"), /* VAR_ERR_INCOMPLETE_INDEX_SPEC
2547 */
2548 _("bracket expression in array variable's index not closed"), /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX
2549 */
2550 _("division by zero error in index specification"), /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX
2551 */
2552 _("unterminated loop construct"), /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
2553 _("invalid character in loop limits"), /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS
2554 */
2555 _("malformed operation argument list"), /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS
2556 */
2557 _("undefined operation"), /* VAR_ERR_UNDEFINED_OPERATION */
2558 _("formatting failure") /* VAR_ERR_FORMATTING_FAILURE */
2559 };
2560
2561 /* translate a return code into its corresponding descriptive text */
var_strerror(var_t * var,var_rc_t rc)2562 const char* var_strerror(var_t* var, var_rc_t rc)
2563 {
2564 const char* str;
2565 rc = (var_rc_t)(0 - rc);
2566 if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char*)) {
2567 str = _("unknown error");
2568 } else {
2569 str = (char*)var_errors[rc];
2570 }
2571 return str;
2572 }
2573