|  | /* An abstract string datatype. | 
|  | Copyright (C) 1998-2022 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 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); | 
|  | } |