UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2025 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "strreplace.h" 30 31 #include <string.h> 32 #include <stdlib.h> 33 #include <ctype.h> 34 35 #include <cx/buffer.h> 36 37 38 39 StringTemplate* string_template_compile(const CxAllocator *a, cxstring tpl) { 40 StringTemplateSegment *end = NULL; // segment list end 41 int var = FALSE; 42 int error = FALSE; 43 44 CxBuffer buf; // tmp buffer 45 if(cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { 46 return NULL; 47 } 48 49 StringTemplate *t = cxMalloc(a, sizeof(StringTemplate)); 50 if(!t) { 51 cxBufferDestroy(&buf); 52 return NULL; 53 } 54 t->a = a ? a : cxDefaultAllocator; 55 t->segments = NULL; 56 57 StringTemplateSegment *seg = NULL; 58 59 for(size_t i=0;i<tpl.length;i++) { 60 char c = tpl.ptr[i]; 61 int add_char = FALSE; // add current char to the buffer 62 int finish_seg = FALSE; // copy buffer to segment string and start new segment 63 64 if(!seg) { 65 // start new segment 66 seg = cxMalloc(a, sizeof(StringTemplateSegment)); 67 if(seg) { 68 seg->type = var ? STRING_SEGMENT_VAR_PLACEHOLDER : STRING_SEGMENT_STR; 69 seg->str = (cxmutstr){NULL, 0}; 70 seg->num = 0; 71 seg->next = NULL; 72 // add segment to segment list 73 if(end) { 74 end->next = seg; 75 } else { 76 t->segments = seg; 77 } 78 end = seg; 79 } else { 80 error = TRUE; 81 break; 82 } 83 } 84 85 if(var) { 86 // current segment is a var 87 if(c == '}') { 88 var = FALSE; 89 finish_seg = TRUE; 90 } else if(c == '{') { 91 // noop 92 } else if(!isalnum(c)) { 93 var = FALSE; 94 finish_seg = TRUE; 95 i--; 96 } else { 97 add_char = TRUE; 98 } 99 } else { 100 if(c == '$') { 101 if(i+1<tpl.length && tpl.ptr[i+1] == '$') { 102 // $$ -> $ 103 i++; 104 add_char = TRUE; 105 } else { 106 var = TRUE; 107 if(buf.pos == 0) { 108 // reuse current segment 109 seg->type = STRING_SEGMENT_VAR_PLACEHOLDER; 110 } else { 111 // create new segment 112 finish_seg = TRUE; 113 } 114 } 115 } else { 116 add_char = TRUE; 117 } 118 } 119 120 if(add_char) { 121 if(cxBufferPut(&buf, c) != c) { 122 error = TRUE; 123 break; 124 } 125 } else if(finish_seg) { 126 // copy buffer content 127 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); 128 if(!seg_str.ptr) { 129 error = TRUE; 130 break; 131 } 132 seg->str = seg_str; 133 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { 134 // is the var segment an integer reference? 135 if(!cx_strtoi(seg_str, &seg->num, 10)) { 136 seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; 137 } 138 } 139 buf.pos = 0; 140 seg = NULL; 141 } 142 } 143 144 // finish last segment 145 if(seg) { 146 cxmutstr seg_str = cx_strdup_a(a, cx_strn(buf.space, buf.pos)); 147 if(!seg_str.ptr) { 148 error = TRUE; 149 } else { 150 seg->str = seg_str; 151 if(seg->type == STRING_SEGMENT_VAR_PLACEHOLDER) { 152 if(!cx_strtoi(seg_str, &seg->num, 10)) { 153 seg->type = STRING_SEGMENT_NUM_PLACEHOLDER; 154 } 155 } 156 } 157 } 158 159 cxBufferDestroy(&buf); 160 if(error) { 161 string_template_free(t); 162 return NULL; 163 } 164 165 return t; 166 } 167 168 void string_template_free(StringTemplate *tpl) { 169 StringTemplateSegment *seg = tpl->segments; 170 while(seg) { 171 StringTemplateSegment *next = seg->next; 172 cxFree(tpl->a, seg->str.ptr); 173 cxFree(tpl->a, seg); 174 seg = next; 175 } 176 cxFree(tpl->a, tpl); 177 } 178 179 ssize_t string_template_write_to(StringTemplate *tpl, const CxAllocator *a, strtpl_var_func varfunc, void *userdata, void *stream, cx_write_func writef) { 180 if(!tpl) { 181 return -1; 182 } 183 184 // write each segment to the stream 185 StringTemplateSegment *seg = tpl->segments; 186 ssize_t w = 0; 187 while(seg) { 188 if(seg->type == STRING_SEGMENT_STR) { 189 // just write the segment string 190 if(seg->str.length > 0) { 191 size_t r = writef(seg->str.ptr, 1, seg->str.length, stream); 192 if(r != seg->str.length) { 193 return -1; 194 } 195 w += r; 196 } 197 } else if(varfunc) { 198 // convert var segment to value 199 WSBool free_str = FALSE; 200 cxmutstr str = varfunc(a, seg, userdata, &free_str); 201 if(str.length > 0) { 202 size_t r = writef(str.ptr, 1, str.length, stream); 203 if(r != str.length) { 204 return -1; 205 } 206 w += r; 207 } 208 if(free_str) { 209 cxFree(a, str.ptr); 210 } 211 } 212 seg = seg->next; 213 } 214 return w; 215 } 216 217 cxmutstr string_template_build_string(StringTemplate *tpl, const CxAllocator *a, strtpl_var_func varfunc, void *userdata) { 218 CxBuffer buf; 219 cxBufferInit(&buf, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 220 221 ssize_t w = string_template_write_to(tpl, a, varfunc, userdata, &buf, (cx_write_func)cxBufferWrite); 222 if(w < 0 || cxBufferTerminate(&buf)) { 223 cxBufferDestroy(&buf); 224 return (cxmutstr){ NULL, 0 }; 225 } 226 return (cxmutstr){ buf.space, buf.size }; 227 } 228