diff -r 392ec9026b07 -r 9728d3a2ac97 src/server/util/strreplace.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/util/strreplace.c Sat Nov 22 16:44:42 2025 +0100 @@ -0,0 +1,227 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "strreplace.h" + +#include +#include +#include + +#include + + + +StringTemplate* string_template_compile(const CxAllocator *a, cxstring tpl) { + StringTemplateSegment *end = NULL; // segment list end + int var = FALSE; + int error = FALSE; + + CxBuffer buf; // tmp buffer + if(cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { + return NULL; + } + + StringTemplate *t = cxMalloc(a, sizeof(StringTemplate)); + if(!t) { + cxBufferDestroy(&buf); + return NULL; + } + t->a = a ? a : cxDefaultAllocator; + t->segments = NULL; + + StringTemplateSegment *seg = NULL; + + for(size_t i=0;itype = var ? STRING_SEGMENT_VAR_PLACEHOLDER : STRING_SEGMENT_STR; + seg->str = (cxmutstr){NULL, 0}; + seg->num = 0; + seg->next = NULL; + // add segment to segment list + if(end) { + end->next = seg; + } else { + t->segments = seg; + } + end = seg; + } else { + error = TRUE; + break; + } + } + + if(var) { + // current segment is a var + if(c == '}') { + var = FALSE; + finish_seg = TRUE; + } else if(c == '{') { + // noop + } else if(!isalnum(c)) { + var = FALSE; + finish_seg = TRUE; + i--; + } else { + add_char = TRUE; + } + } else { + if(c == '$') { + if(i+1 $ + i++; + add_char = TRUE; + } else { + var = TRUE; + if(buf.pos == 0) { + // reuse current segment + seg->type = STRING_SEGMENT_VAR_PLACEHOLDER; + } else { + // create new segment + finish_seg = TRUE; + } + } + } else { + add_char = TRUE; + } + } + + if(add_char) { + if(cxBufferPut(&buf, c) != c) { + error = TRUE; + break; + } + } else if(finish_seg) { + // copy buffer content + cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); + if(!seg_str.ptr) { + error = TRUE; + break; + } + seg->str = seg_str; + if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { + // is the var segment an integer reference? + if(!cx_strtoi(seg_str, &seg->num, 10)) { + seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; + } + } + buf.pos = 0; + seg = NULL; + } + } + + // finish last segment + if(seg) { + cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); + if(!seg_str.ptr) { + error = TRUE; + } else { + seg->str = seg_str; + if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { + if(!cx_strtoi(seg_str, &seg->num, 10)) { + seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; + } + } + } + } + + cxBufferDestroy(&buf); + if(error) { + string_template_free(t); + return NULL; + } + + return t; +} + +void string_template_free(StringTemplate *tpl) { + StringTemplateSegment *seg = tpl->segments; + while(seg) { + StringTemplateSegment *next = seg->next; + cxFree(tpl->a, seg->str.ptr); + cxFree(tpl->a, seg); + seg = next; + } + cxFree(tpl->a, tpl); +} + +ssize_t string_template_write_to(StringTemplate *tpl, const CxAllocator *a, strtpl_var_func varfunc, void *userdata, void *stream, cx_write_func writef) { + if(!tpl) { + return -1; + } + + // write each segment to the stream + StringTemplateSegment *seg = tpl->segments; + ssize_t w = 0; + while(seg) { + if(seg->type == STRING_SEGMENT_STR) { + // just write the segment string + if(seg->str.length > 0) { + size_t r = writef(seg->str.ptr, 1, seg->str.length, stream); + if(r != seg->str.length) { + return -1; + } + w += r; + } + } else if(varfunc) { + // convert var segment to value + WSBool free_str = FALSE; + cxmutstr str = varfunc(a, seg, userdata, &free_str); + if(str.length > 0) { + size_t r = writef(str.ptr, 1, str.length, stream); + if(r != str.length) { + return -1; + } + w += r; + } + if(free_str) { + cxFree(a, str.ptr); + } + } + seg = seg->next; + } + return w; +} + +cxmutstr string_template_build_string(StringTemplate *tpl, const CxAllocator *a, strtpl_var_func varfunc, void *userdata) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); + + ssize_t w = string_template_write_to(tpl, a, varfunc, userdata, &buf, (cx_write_func)cxBufferWrite); + if(w < 0 || cxBufferTerminate(&buf)) { + cxBufferDestroy(&buf); + return (cxmutstr){ NULL, 0 }; + } + return (cxmutstr){ buf.space, buf.size }; +}