stringbuf.c
1 /** 2 * resizable string buffer 3 * 4 * (c) 2017-2020 Steve Bennett <steveb@workware.net.au> 5 * 6 * See utf8.c for licence details. 7 */ 8 #include <stdlib.h> 9 #include <string.h> 10 #include <stdio.h> 11 #include <ctype.h> 12 #include <assert.h> 13 14 #ifndef STRINGBUF_H 15 #include "stringbuf.h" 16 #endif 17 #ifdef USE_UTF8 18 #ifndef UTF8_UTIL_H 19 #include "utf8.h" 20 #endif 21 #endif 22 23 #define SB_INCREMENT 200 24 25 stringbuf *sb_alloc(void) 26 { 27 stringbuf *sb = (stringbuf *)malloc(sizeof(*sb)); 28 sb->remaining = 0; 29 sb->last = 0; 30 #ifdef USE_UTF8 31 sb->chars = 0; 32 #endif 33 sb->data = NULL; 34 35 return(sb); 36 } 37 38 void sb_free(stringbuf *sb) 39 { 40 if (sb) { 41 free(sb->data); 42 } 43 free(sb); 44 } 45 46 static void sb_realloc(stringbuf *sb, int newlen) 47 { 48 sb->data = (char *)realloc(sb->data, newlen); 49 sb->remaining = newlen - sb->last; 50 } 51 52 void sb_append(stringbuf *sb, const char *str) 53 { 54 sb_append_len(sb, str, strlen(str)); 55 } 56 57 void sb_append_len(stringbuf *sb, const char *str, int len) 58 { 59 if (sb->remaining < len + 1) { 60 sb_realloc(sb, sb->last + len + 1 + SB_INCREMENT); 61 } 62 memcpy(sb->data + sb->last, str, len); 63 sb->data[sb->last + len] = 0; 64 65 sb->last += len; 66 sb->remaining -= len; 67 #ifdef USE_UTF8 68 sb->chars += utf8_strlen(str, len); 69 #endif 70 } 71 72 char *sb_to_string(stringbuf *sb) 73 { 74 if (sb->data == NULL) { 75 /* Return an allocated empty string, not null */ 76 return strdup(""); 77 } 78 else { 79 /* Just return the data and free the stringbuf structure */ 80 char *pt = sb->data; 81 free(sb); 82 return pt; 83 } 84 } 85 86 /* Insert and delete operations */ 87 88 /* Moves up all the data at position 'pos' and beyond by 'len' bytes 89 * to make room for new data 90 * 91 * Note: Does *not* update sb->chars 92 */ 93 static void sb_insert_space(stringbuf *sb, int pos, int len) 94 { 95 assert(pos <= sb->last); 96 97 /* Make sure there is enough space */ 98 if (sb->remaining < len) { 99 sb_realloc(sb, sb->last + len + SB_INCREMENT); 100 } 101 /* Now move it up */ 102 memmove(sb->data + pos + len, sb->data + pos, sb->last - pos); 103 sb->last += len; 104 sb->remaining -= len; 105 /* And null terminate */ 106 sb->data[sb->last] = 0; 107 } 108 109 /** 110 * Move down all the data from pos + len, effectively 111 * deleting the data at position 'pos' of length 'len' 112 */ 113 static void sb_delete_space(stringbuf *sb, int pos, int len) 114 { 115 assert(pos < sb->last); 116 assert(pos + len <= sb->last); 117 118 #ifdef USE_UTF8 119 sb->chars -= utf8_strlen(sb->data + pos, len); 120 #endif 121 122 /* Now move it up */ 123 memmove(sb->data + pos, sb->data + pos + len, sb->last - pos - len); 124 sb->last -= len; 125 sb->remaining += len; 126 /* And null terminate */ 127 sb->data[sb->last] = 0; 128 } 129 130 void sb_insert(stringbuf *sb, int index, const char *str) 131 { 132 if (index >= sb->last) { 133 /* Inserting after the end of the list appends. */ 134 sb_append(sb, str); 135 } 136 else { 137 int len = strlen(str); 138 139 sb_insert_space(sb, index, len); 140 memcpy(sb->data + index, str, len); 141 #ifdef USE_UTF8 142 sb->chars += utf8_strlen(str, len); 143 #endif 144 } 145 } 146 147 /** 148 * Delete the bytes at index 'index' for length 'len' 149 * Has no effect if the index is past the end of the list. 150 */ 151 void sb_delete(stringbuf *sb, int index, int len) 152 { 153 if (index < sb->last) { 154 char *pos = sb->data + index; 155 if (len < 0) { 156 len = sb->last; 157 } 158 159 sb_delete_space(sb, pos - sb->data, len); 160 } 161 } 162 163 void sb_clear(stringbuf *sb) 164 { 165 if (sb->data) { 166 /* Null terminate */ 167 sb->data[0] = 0; 168 sb->last = 0; 169 #ifdef USE_UTF8 170 sb->chars = 0; 171 #endif 172 } 173 }