1 | /* An abstract string datatype. |
2 | Copyright (C) 1998-2024 Free Software Foundation, Inc. |
3 | Contributed by Mark Mitchell (mark@markmitchell.com). |
4 | |
5 | This file is part of GNU CC. |
6 | |
7 | GNU CC is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 2, or (at your option) |
10 | any later version. |
11 | |
12 | In addition to the permissions in the GNU General Public License, the |
13 | Free Software Foundation gives you unlimited permission to link the |
14 | compiled version of this file into combinations with other programs, |
15 | and to distribute those combinations without any restriction coming |
16 | from the use of this file. (The General Public License restrictions |
17 | do apply in other respects; for example, they cover modification of |
18 | the file, and distribution when not linked into a combined |
19 | executable.) |
20 | |
21 | GNU CC is distributed in the hope that it will be useful, |
22 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 | GNU General Public License for more details. |
25 | |
26 | You should have received a copy of the GNU General Public License |
27 | along with GNU CC; see the file COPYING. If not, write to |
28 | the Free Software Foundation, 51 Franklin Street - Fifth Floor, |
29 | Boston, MA 02110-1301, USA. */ |
30 | |
31 | #ifdef HAVE_CONFIG_H |
32 | #include "config.h" |
33 | #endif |
34 | |
35 | #include <stdio.h> |
36 | |
37 | #ifdef HAVE_STRING_H |
38 | #include <string.h> |
39 | #endif |
40 | |
41 | #ifdef HAVE_STDLIB_H |
42 | #include <stdlib.h> |
43 | #endif |
44 | |
45 | #include "libiberty.h" |
46 | #include "dyn-string.h" |
47 | |
48 | /* Performs in-place initialization of a dyn_string struct. This |
49 | function can be used with a dyn_string struct on the stack or |
50 | embedded in another object. The contents of the string itself |
51 | are still dynamically allocated. The string initially is capable |
52 | of holding at least SPACE characeters, including the terminating |
53 | NUL. If SPACE is 0, it will silently be increated to 1. |
54 | |
55 | If RETURN_ON_ALLOCATION_FAILURE is defined and memory allocation |
56 | fails, returns 0. Otherwise returns 1. */ |
57 | |
58 | int |
59 | dyn_string_init (struct dyn_string *ds_struct_ptr, int space) |
60 | { |
61 | /* We need at least one byte in which to store the terminating NUL. */ |
62 | if (space == 0) |
63 | space = 1; |
64 | |
65 | #ifdef RETURN_ON_ALLOCATION_FAILURE |
66 | ds_struct_ptr->s = (char *) malloc (space); |
67 | if (ds_struct_ptr->s == NULL) |
68 | return 0; |
69 | #else |
70 | ds_struct_ptr->s = XNEWVEC (char, space); |
71 | #endif |
72 | ds_struct_ptr->allocated = space; |
73 | ds_struct_ptr->length = 0; |
74 | ds_struct_ptr->s[0] = '\0'; |
75 | |
76 | return 1; |
77 | } |
78 | |
79 | /* Create a new dynamic string capable of holding at least SPACE |
80 | characters, including the terminating NUL. If SPACE is 0, it will |
81 | be silently increased to 1. If RETURN_ON_ALLOCATION_FAILURE is |
82 | defined and memory allocation fails, returns NULL. Otherwise |
83 | returns the newly allocated string. */ |
84 | |
85 | dyn_string_t |
86 | dyn_string_new (int space) |
87 | { |
88 | dyn_string_t result; |
89 | #ifdef RETURN_ON_ALLOCATION_FAILURE |
90 | result = (dyn_string_t) malloc (sizeof (struct dyn_string)); |
91 | if (result == NULL) |
92 | return NULL; |
93 | if (!dyn_string_init (result, space)) |
94 | { |
95 | free (result); |
96 | return NULL; |
97 | } |
98 | #else |
99 | result = XNEW (struct dyn_string); |
100 | dyn_string_init (ds_struct_ptr: result, space); |
101 | #endif |
102 | return result; |
103 | } |
104 | |
105 | /* Free the memory used by DS. */ |
106 | |
107 | void |
108 | dyn_string_delete (dyn_string_t ds) |
109 | { |
110 | free (ptr: ds->s); |
111 | free (ptr: ds); |
112 | } |
113 | |
114 | /* Returns the contents of DS in a buffer allocated with malloc. It |
115 | is the caller's responsibility to deallocate the buffer using free. |
116 | DS is then set to the empty string. Deletes DS itself. */ |
117 | |
118 | char* |
119 | dyn_string_release (dyn_string_t ds) |
120 | { |
121 | /* Store the old buffer. */ |
122 | char* result = ds->s; |
123 | /* The buffer is no longer owned by DS. */ |
124 | ds->s = NULL; |
125 | /* Delete DS. */ |
126 | free (ptr: ds); |
127 | /* Return the old buffer. */ |
128 | return result; |
129 | } |
130 | |
131 | /* Increase the capacity of DS so it can hold at least SPACE |
132 | characters, plus the terminating NUL. This function will not (at |
133 | present) reduce the capacity of DS. Returns DS on success. |
134 | |
135 | If RETURN_ON_ALLOCATION_FAILURE is defined and a memory allocation |
136 | operation fails, deletes DS and returns NULL. */ |
137 | |
138 | dyn_string_t |
139 | dyn_string_resize (dyn_string_t ds, int space) |
140 | { |
141 | int new_allocated = ds->allocated; |
142 | |
143 | /* Increase SPACE to hold the NUL termination. */ |
144 | ++space; |
145 | |
146 | /* Increase allocation by factors of two. */ |
147 | while (space > new_allocated) |
148 | new_allocated *= 2; |
149 | |
150 | if (new_allocated != ds->allocated) |
151 | { |
152 | ds->allocated = new_allocated; |
153 | /* We actually need more space. */ |
154 | #ifdef RETURN_ON_ALLOCATION_FAILURE |
155 | ds->s = (char *) realloc (ds->s, ds->allocated); |
156 | if (ds->s == NULL) |
157 | { |
158 | free (ds); |
159 | return NULL; |
160 | } |
161 | #else |
162 | ds->s = XRESIZEVEC (char, ds->s, ds->allocated); |
163 | #endif |
164 | } |
165 | |
166 | return ds; |
167 | } |
168 | |
169 | /* Sets the contents of DS to the empty string. */ |
170 | |
171 | void |
172 | dyn_string_clear (dyn_string_t ds) |
173 | { |
174 | /* A dyn_string always has room for at least the NUL terminator. */ |
175 | ds->s[0] = '\0'; |
176 | ds->length = 0; |
177 | } |
178 | |
179 | /* Makes the contents of DEST the same as the contents of SRC. DEST |
180 | and SRC must be distinct. Returns 1 on success. On failure, if |
181 | RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ |
182 | |
183 | int |
184 | dyn_string_copy (dyn_string_t dest, dyn_string_t src) |
185 | { |
186 | if (dest == src) |
187 | abort (); |
188 | |
189 | /* Make room in DEST. */ |
190 | if (dyn_string_resize (ds: dest, space: src->length) == NULL) |
191 | return 0; |
192 | /* Copy DEST into SRC. */ |
193 | strcpy (dest: dest->s, src: src->s); |
194 | /* Update the size of DEST. */ |
195 | dest->length = src->length; |
196 | return 1; |
197 | } |
198 | |
199 | /* Copies SRC, a NUL-terminated string, into DEST. Returns 1 on |
200 | success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST |
201 | and returns 0. */ |
202 | |
203 | int |
204 | dyn_string_copy_cstr (dyn_string_t dest, const char *src) |
205 | { |
206 | int length = strlen (s: src); |
207 | /* Make room in DEST. */ |
208 | if (dyn_string_resize (ds: dest, space: length) == NULL) |
209 | return 0; |
210 | /* Copy DEST into SRC. */ |
211 | strcpy (dest: dest->s, src: src); |
212 | /* Update the size of DEST. */ |
213 | dest->length = length; |
214 | return 1; |
215 | } |
216 | |
217 | /* Inserts SRC at the beginning of DEST. DEST is expanded as |
218 | necessary. SRC and DEST must be distinct. Returns 1 on success. |
219 | On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and |
220 | returns 0. */ |
221 | |
222 | int |
223 | dyn_string_prepend (dyn_string_t dest, dyn_string_t src) |
224 | { |
225 | return dyn_string_insert (dest, 0, src); |
226 | } |
227 | |
228 | /* Inserts SRC, a NUL-terminated string, at the beginning of DEST. |
229 | DEST is expanded as necessary. Returns 1 on success. On failure, |
230 | if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ |
231 | |
232 | int |
233 | dyn_string_prepend_cstr (dyn_string_t dest, const char *src) |
234 | { |
235 | return dyn_string_insert_cstr (dest, 0, src); |
236 | } |
237 | |
238 | /* Inserts SRC into DEST starting at position POS. DEST is expanded |
239 | as necessary. SRC and DEST must be distinct. Returns 1 on |
240 | success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST |
241 | and returns 0. */ |
242 | |
243 | int |
244 | dyn_string_insert (dyn_string_t dest, int pos, dyn_string_t src) |
245 | { |
246 | int i; |
247 | |
248 | if (src == dest) |
249 | abort (); |
250 | |
251 | if (dyn_string_resize (ds: dest, space: dest->length + src->length) == NULL) |
252 | return 0; |
253 | /* Make room for the insertion. Be sure to copy the NUL. */ |
254 | for (i = dest->length; i >= pos; --i) |
255 | dest->s[i + src->length] = dest->s[i]; |
256 | /* Splice in the new stuff. */ |
257 | strncpy (dest: dest->s + pos, src: src->s, n: src->length); |
258 | /* Compute the new length. */ |
259 | dest->length += src->length; |
260 | return 1; |
261 | } |
262 | |
263 | /* Inserts SRC, a NUL-terminated string, into DEST starting at |
264 | position POS. DEST is expanded as necessary. Returns 1 on |
265 | success. On failure, RETURN_ON_ALLOCATION_FAILURE, deletes DEST |
266 | and returns 0. */ |
267 | |
268 | int |
269 | dyn_string_insert_cstr (dyn_string_t dest, int pos, const char *src) |
270 | { |
271 | int i; |
272 | int length = strlen (s: src); |
273 | |
274 | if (dyn_string_resize (ds: dest, space: dest->length + length) == NULL) |
275 | return 0; |
276 | /* Make room for the insertion. Be sure to copy the NUL. */ |
277 | for (i = dest->length; i >= pos; --i) |
278 | dest->s[i + length] = dest->s[i]; |
279 | /* Splice in the new stuff. */ |
280 | memcpy (dest: dest->s + pos, src: src, n: length); |
281 | /* Compute the new length. */ |
282 | dest->length += length; |
283 | return 1; |
284 | } |
285 | |
286 | /* Inserts character C into DEST starting at position POS. DEST is |
287 | expanded as necessary. Returns 1 on success. On failure, |
288 | RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ |
289 | |
290 | int |
291 | dyn_string_insert_char (dyn_string_t dest, int pos, int c) |
292 | { |
293 | int i; |
294 | |
295 | if (dyn_string_resize (ds: dest, space: dest->length + 1) == NULL) |
296 | return 0; |
297 | /* Make room for the insertion. Be sure to copy the NUL. */ |
298 | for (i = dest->length; i >= pos; --i) |
299 | dest->s[i + 1] = dest->s[i]; |
300 | /* Add the new character. */ |
301 | dest->s[pos] = c; |
302 | /* Compute the new length. */ |
303 | ++dest->length; |
304 | return 1; |
305 | } |
306 | |
307 | /* Append S to DS, resizing DS if necessary. Returns 1 on success. |
308 | On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and |
309 | returns 0. */ |
310 | |
311 | int |
312 | dyn_string_append (dyn_string_t dest, dyn_string_t s) |
313 | { |
314 | if (dyn_string_resize (ds: dest, space: dest->length + s->length) == 0) |
315 | return 0; |
316 | strcpy (dest: dest->s + dest->length, src: s->s); |
317 | dest->length += s->length; |
318 | return 1; |
319 | } |
320 | |
321 | /* Append the NUL-terminated string S to DS, resizing DS if necessary. |
322 | Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, |
323 | deletes DEST and returns 0. */ |
324 | |
325 | int |
326 | dyn_string_append_cstr (dyn_string_t dest, const char *s) |
327 | { |
328 | int len = strlen (s: s); |
329 | |
330 | /* The new length is the old length plus the size of our string, plus |
331 | one for the null at the end. */ |
332 | if (dyn_string_resize (ds: dest, space: dest->length + len) == NULL) |
333 | return 0; |
334 | strcpy (dest: dest->s + dest->length, src: s); |
335 | dest->length += len; |
336 | return 1; |
337 | } |
338 | |
339 | /* Appends C to the end of DEST. Returns 1 on success. On failure, |
340 | if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ |
341 | |
342 | int |
343 | dyn_string_append_char (dyn_string_t dest, int c) |
344 | { |
345 | /* Make room for the extra character. */ |
346 | if (dyn_string_resize (ds: dest, space: dest->length + 1) == NULL) |
347 | return 0; |
348 | /* Append the character; it will overwrite the old NUL. */ |
349 | dest->s[dest->length] = c; |
350 | /* Add a new NUL at the end. */ |
351 | dest->s[dest->length + 1] = '\0'; |
352 | /* Update the length. */ |
353 | ++(dest->length); |
354 | return 1; |
355 | } |
356 | |
357 | /* Sets the contents of DEST to the substring of SRC starting at START |
358 | and ending before END. START must be less than or equal to END, |
359 | and both must be between zero and the length of SRC, inclusive. |
360 | Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, |
361 | deletes DEST and returns 0. */ |
362 | |
363 | int |
364 | dyn_string_substring (dyn_string_t dest, dyn_string_t src, |
365 | int start, int end) |
366 | { |
367 | int i; |
368 | int length = end - start; |
369 | |
370 | if (start > end || start > src->length || end > src->length) |
371 | abort (); |
372 | |
373 | /* Make room for the substring. */ |
374 | if (dyn_string_resize (ds: dest, space: length) == NULL) |
375 | return 0; |
376 | /* Copy the characters in the substring, */ |
377 | for (i = length; --i >= 0; ) |
378 | dest->s[i] = src->s[start + i]; |
379 | /* NUL-terimate the result. */ |
380 | dest->s[length] = '\0'; |
381 | /* Record the length of the substring. */ |
382 | dest->length = length; |
383 | |
384 | return 1; |
385 | } |
386 | |
387 | /* Returns non-zero if DS1 and DS2 have the same contents. */ |
388 | |
389 | int |
390 | dyn_string_eq (dyn_string_t ds1, dyn_string_t ds2) |
391 | { |
392 | /* If DS1 and DS2 have different lengths, they must not be the same. */ |
393 | if (ds1->length != ds2->length) |
394 | return 0; |
395 | else |
396 | return !strcmp (s1: ds1->s, s2: ds2->s); |
397 | } |
398 | |