| /* An abstract string datatype. | |
| Copyright (C) 1998-2025 Free Software Foundation, Inc. | |
| Contributed by Mark Mitchell (mark@markmitchell.com). | |
| This file is part of GNU CC. | |
| GNU CC is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| In addition to the permissions in the GNU General Public License, the | |
| Free Software Foundation gives you unlimited permission to link the | |
| compiled version of this file into combinations with other programs, | |
| and to distribute those combinations without any restriction coming | |
| from the use of this file. (The General Public License restrictions | |
| do apply in other respects; for example, they cover modification of | |
| the file, and distribution when not linked into a combined | |
| executable.) | |
| GNU CC is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU CC; see the file COPYING. If not, write to | |
| the Free Software Foundation, 51 Franklin Street - Fifth Floor, | |
| Boston, MA 02110-1301, USA. */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include <stdio.h> | |
| #ifdef HAVE_STRING_H | |
| #include <string.h> | |
| #endif | |
| #ifdef HAVE_STDLIB_H | |
| #include <stdlib.h> | |
| #endif | |
| #include "libiberty.h" | |
| #include "dyn-string.h" | |
| /* Performs in-place initialization of a dyn_string struct. This | |
| function can be used with a dyn_string struct on the stack or | |
| embedded in another object. The contents of the string itself | |
| are still dynamically allocated. The string initially is capable | |
| of holding at least SPACE characeters, including the terminating | |
| NUL. If SPACE is 0, it will silently be increated to 1. | |
| If RETURN_ON_ALLOCATION_FAILURE is defined and memory allocation | |
| fails, returns 0. Otherwise returns 1. */ | |
| int | |
| dyn_string_init (struct dyn_string *ds_struct_ptr, int space) | |
| { | |
| /* We need at least one byte in which to store the terminating NUL. */ | |
| if (space == 0) | |
| space = 1; | |
| #ifdef RETURN_ON_ALLOCATION_FAILURE | |
| ds_struct_ptr->s = (char *) malloc (space); | |
| if (ds_struct_ptr->s == NULL) | |
| return 0; | |
| #else | |
| ds_struct_ptr->s = XNEWVEC (char, space); | |
| #endif | |
| ds_struct_ptr->allocated = space; | |
| ds_struct_ptr->length = 0; | |
| ds_struct_ptr->s[0] = '\0'; | |
| return 1; | |
| } | |
| /* Create a new dynamic string capable of holding at least SPACE | |
| characters, including the terminating NUL. If SPACE is 0, it will | |
| be silently increased to 1. If RETURN_ON_ALLOCATION_FAILURE is | |
| defined and memory allocation fails, returns NULL. Otherwise | |
| returns the newly allocated string. */ | |
| dyn_string_t | |
| dyn_string_new (int space) | |
| { | |
| dyn_string_t result; | |
| #ifdef RETURN_ON_ALLOCATION_FAILURE | |
| result = (dyn_string_t) malloc (sizeof (struct dyn_string)); | |
| if (result == NULL) | |
| return NULL; | |
| if (!dyn_string_init (result, space)) | |
| { | |
| free (result); | |
| return NULL; | |
| } | |
| #else | |
| result = XNEW (struct dyn_string); | |
| dyn_string_init (result, space); | |
| #endif | |
| return result; | |
| } | |
| /* Free the memory used by DS. */ | |
| void | |
| dyn_string_delete (dyn_string_t ds) | |
| { | |
| free (ds->s); | |
| free (ds); | |
| } | |
| /* Returns the contents of DS in a buffer allocated with malloc. It | |
| is the caller's responsibility to deallocate the buffer using free. | |
| DS is then set to the empty string. Deletes DS itself. */ | |
| char* | |
| dyn_string_release (dyn_string_t ds) | |
| { | |
| /* Store the old buffer. */ | |
| char* result = ds->s; | |
| /* The buffer is no longer owned by DS. */ | |
| ds->s = NULL; | |
| /* Delete DS. */ | |
| free (ds); | |
| /* Return the old buffer. */ | |
| return result; | |
| } | |
| /* Increase the capacity of DS so it can hold at least SPACE | |
| characters, plus the terminating NUL. This function will not (at | |
| present) reduce the capacity of DS. Returns DS on success. | |
| If RETURN_ON_ALLOCATION_FAILURE is defined and a memory allocation | |
| operation fails, deletes DS and returns NULL. */ | |
| dyn_string_t | |
| dyn_string_resize (dyn_string_t ds, int space) | |
| { | |
| int new_allocated = ds->allocated; | |
| /* Increase SPACE to hold the NUL termination. */ | |
| ++space; | |
| /* Increase allocation by factors of two. */ | |
| while (space > new_allocated) | |
| new_allocated *= 2; | |
| if (new_allocated != ds->allocated) | |
| { | |
| ds->allocated = new_allocated; | |
| /* We actually need more space. */ | |
| #ifdef RETURN_ON_ALLOCATION_FAILURE | |
| ds->s = (char *) realloc (ds->s, ds->allocated); | |
| if (ds->s == NULL) | |
| { | |
| free (ds); | |
| return NULL; | |
| } | |
| #else | |
| ds->s = XRESIZEVEC (char, ds->s, ds->allocated); | |
| #endif | |
| } | |
| return ds; | |
| } | |
| /* Sets the contents of DS to the empty string. */ | |
| void | |
| dyn_string_clear (dyn_string_t ds) | |
| { | |
| /* A dyn_string always has room for at least the NUL terminator. */ | |
| ds->s[0] = '\0'; | |
| ds->length = 0; | |
| } | |
| /* Makes the contents of DEST the same as the contents of SRC. DEST | |
| and SRC must be distinct. Returns 1 on success. On failure, if | |
| RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ | |
| int | |
| dyn_string_copy (dyn_string_t dest, dyn_string_t src) | |
| { | |
| if (dest == src) | |
| abort (); | |
| /* Make room in DEST. */ | |
| if (dyn_string_resize (dest, src->length) == NULL) | |
| return 0; | |
| /* Copy DEST into SRC. */ | |
| strcpy (dest->s, src->s); | |
| /* Update the size of DEST. */ | |
| dest->length = src->length; | |
| return 1; | |
| } | |
| /* Copies SRC, a NUL-terminated string, into DEST. Returns 1 on | |
| success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST | |
| and returns 0. */ | |
| int | |
| dyn_string_copy_cstr (dyn_string_t dest, const char *src) | |
| { | |
| int length = strlen (src); | |
| /* Make room in DEST. */ | |
| if (dyn_string_resize (dest, length) == NULL) | |
| return 0; | |
| /* Copy DEST into SRC. */ | |
| strcpy (dest->s, src); | |
| /* Update the size of DEST. */ | |
| dest->length = length; | |
| return 1; | |
| } | |
| /* Inserts SRC at the beginning of DEST. DEST is expanded as | |
| necessary. SRC and DEST must be distinct. Returns 1 on success. | |
| On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and | |
| returns 0. */ | |
| int | |
| dyn_string_prepend (dyn_string_t dest, dyn_string_t src) | |
| { | |
| return dyn_string_insert (dest, 0, src); | |
| } | |
| /* Inserts SRC, a NUL-terminated string, at the beginning of DEST. | |
| DEST is expanded as necessary. Returns 1 on success. On failure, | |
| if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ | |
| int | |
| dyn_string_prepend_cstr (dyn_string_t dest, const char *src) | |
| { | |
| return dyn_string_insert_cstr (dest, 0, src); | |
| } | |
| /* Inserts SRC into DEST starting at position POS. DEST is expanded | |
| as necessary. SRC and DEST must be distinct. Returns 1 on | |
| success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST | |
| and returns 0. */ | |
| int | |
| dyn_string_insert (dyn_string_t dest, int pos, dyn_string_t src) | |
| { | |
| int i; | |
| if (src == dest) | |
| abort (); | |
| if (dyn_string_resize (dest, dest->length + src->length) == NULL) | |
| return 0; | |
| /* Make room for the insertion. Be sure to copy the NUL. */ | |
| for (i = dest->length; i >= pos; --i) | |
| dest->s[i + src->length] = dest->s[i]; | |
| /* Splice in the new stuff. */ | |
| strncpy (dest->s + pos, src->s, src->length); | |
| /* Compute the new length. */ | |
| dest->length += src->length; | |
| return 1; | |
| } | |
| /* Inserts SRC, a NUL-terminated string, into DEST starting at | |
| position POS. DEST is expanded as necessary. Returns 1 on | |
| success. On failure, RETURN_ON_ALLOCATION_FAILURE, deletes DEST | |
| and returns 0. */ | |
| int | |
| dyn_string_insert_cstr (dyn_string_t dest, int pos, const char *src) | |
| { | |
| int i; | |
| int length = strlen (src); | |
| if (dyn_string_resize (dest, dest->length + length) == NULL) | |
| return 0; | |
| /* Make room for the insertion. Be sure to copy the NUL. */ | |
| for (i = dest->length; i >= pos; --i) | |
| dest->s[i + length] = dest->s[i]; | |
| /* Splice in the new stuff. */ | |
| memcpy (dest->s + pos, src, length); | |
| /* Compute the new length. */ | |
| dest->length += length; | |
| return 1; | |
| } | |
| /* Inserts character C into DEST starting at position POS. DEST is | |
| expanded as necessary. Returns 1 on success. On failure, | |
| RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ | |
| int | |
| dyn_string_insert_char (dyn_string_t dest, int pos, int c) | |
| { | |
| int i; | |
| if (dyn_string_resize (dest, dest->length + 1) == NULL) | |
| return 0; | |
| /* Make room for the insertion. Be sure to copy the NUL. */ | |
| for (i = dest->length; i >= pos; --i) | |
| dest->s[i + 1] = dest->s[i]; | |
| /* Add the new character. */ | |
| dest->s[pos] = c; | |
| /* Compute the new length. */ | |
| ++dest->length; | |
| return 1; | |
| } | |
| /* Append S to DS, resizing DS if necessary. Returns 1 on success. | |
| On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and | |
| returns 0. */ | |
| int | |
| dyn_string_append (dyn_string_t dest, dyn_string_t s) | |
| { | |
| if (dyn_string_resize (dest, dest->length + s->length) == 0) | |
| return 0; | |
| strcpy (dest->s + dest->length, s->s); | |
| dest->length += s->length; | |
| return 1; | |
| } | |
| /* Append the NUL-terminated string S to DS, resizing DS if necessary. | |
| Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, | |
| deletes DEST and returns 0. */ | |
| int | |
| dyn_string_append_cstr (dyn_string_t dest, const char *s) | |
| { | |
| int len = strlen (s); | |
| /* The new length is the old length plus the size of our string, plus | |
| one for the null at the end. */ | |
| if (dyn_string_resize (dest, dest->length + len) == NULL) | |
| return 0; | |
| strcpy (dest->s + dest->length, s); | |
| dest->length += len; | |
| return 1; | |
| } | |
| /* Appends C to the end of DEST. Returns 1 on success. On failure, | |
| if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ | |
| int | |
| dyn_string_append_char (dyn_string_t dest, int c) | |
| { | |
| /* Make room for the extra character. */ | |
| if (dyn_string_resize (dest, dest->length + 1) == NULL) | |
| return 0; | |
| /* Append the character; it will overwrite the old NUL. */ | |
| dest->s[dest->length] = c; | |
| /* Add a new NUL at the end. */ | |
| dest->s[dest->length + 1] = '\0'; | |
| /* Update the length. */ | |
| ++(dest->length); | |
| return 1; | |
| } | |
| /* Sets the contents of DEST to the substring of SRC starting at START | |
| and ending before END. START must be less than or equal to END, | |
| and both must be between zero and the length of SRC, inclusive. | |
| Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, | |
| deletes DEST and returns 0. */ | |
| int | |
| dyn_string_substring (dyn_string_t dest, dyn_string_t src, | |
| int start, int end) | |
| { | |
| int i; | |
| int length = end - start; | |
| if (start > end || start > src->length || end > src->length) | |
| abort (); | |
| /* Make room for the substring. */ | |
| if (dyn_string_resize (dest, length) == NULL) | |
| return 0; | |
| /* Copy the characters in the substring, */ | |
| for (i = length; --i >= 0; ) | |
| dest->s[i] = src->s[start + i]; | |
| /* NUL-terimate the result. */ | |
| dest->s[length] = '\0'; | |
| /* Record the length of the substring. */ | |
| dest->length = length; | |
| return 1; | |
| } | |
| /* Returns non-zero if DS1 and DS2 have the same contents. */ | |
| int | |
| dyn_string_eq (dyn_string_t ds1, dyn_string_t ds2) | |
| { | |
| /* If DS1 and DS2 have different lengths, they must not be the same. */ | |
| if (ds1->length != ds2->length) | |
| return 0; | |
| else | |
| return !strcmp (ds1->s, ds2->s); | |
| } |