+
+/* Obtain a C string from the inferior storing it in a newly allocated
+ buffer in BUFFER, which should be freed by the caller. If the
+ in- and out-parameter *LENGTH is specified at -1, the string is read
+ until a null character of the appropriate width is found, otherwise
+ the string is read to the length of characters specified.
+ The size of a character is determined by the length of the target
+ type of the pointer or array. If VALUE is an array with a known
+ length, the function will not read past the end of the array.
+ On completion, *LENGTH will be set to the size of the string read in
+ characters. (If a length of -1 is specified, the length returned
+ will not include the null character). CHARSET is always set to the
+ target charset. */
+
+void
+c_get_string (struct value *value, gdb_byte **buffer, int *length,
+ struct type **char_type, const char **charset)
+{
+ int err, width;
+ unsigned int fetchlimit;
+ struct type *type = check_typedef (value_type (value));
+ struct type *element_type = TYPE_TARGET_TYPE (type);
+ int req_length = *length;
+ enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
+ enum c_string_type kind;
+
+ if (element_type == NULL)
+ goto error;
+
+ if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+ {
+ /* If we know the size of the array, we can use it as a limit on the
+ number of characters to be fetched. */
+ if (TYPE_NFIELDS (type) == 1
+ && TYPE_CODE (TYPE_FIELD_TYPE (type, 0)) == TYPE_CODE_RANGE)
+ {
+ LONGEST low_bound, high_bound;
+
+ get_discrete_bounds (TYPE_FIELD_TYPE (type, 0),
+ &low_bound, &high_bound);
+ fetchlimit = high_bound - low_bound + 1;
+ }
+ else
+ fetchlimit = UINT_MAX;
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_PTR)
+ fetchlimit = UINT_MAX;
+ else
+ /* We work only with arrays and pointers. */
+ goto error;
+
+ if (! c_textual_element_type (element_type, 0))
+ goto error;
+ kind = classify_type (element_type,
+ get_type_arch (element_type),
+ charset);
+ width = TYPE_LENGTH (element_type);
+
+ /* If the string lives in GDB's memory instead of the inferior's, then we
+ just need to copy it to BUFFER. Also, since such strings are arrays
+ with known size, FETCHLIMIT will hold the size of the array. */
+ if ((VALUE_LVAL (value) == not_lval
+ || VALUE_LVAL (value) == lval_internalvar)
+ && fetchlimit != UINT_MAX)
+ {
+ int i;
+ const gdb_byte *contents = value_contents (value);
+
+ /* If a length is specified, use that. */
+ if (*length >= 0)
+ i = *length;
+ else
+ /* Otherwise, look for a null character. */
+ for (i = 0; i < fetchlimit; i++)
+ if (extract_unsigned_integer (contents + i * width, width,
+ byte_order) == 0)
+ break;
+
+ /* I is now either a user-defined length, the number of non-null
+ characters, or FETCHLIMIT. */
+ *length = i * width;
+ *buffer = xmalloc (*length);
+ memcpy (*buffer, contents, *length);
+ err = 0;
+ }
+ else
+ {
+ err = read_string (value_as_address (value), *length, width, fetchlimit,
+ byte_order, buffer, length);
+ if (err)
+ {
+ xfree (*buffer);
+ error (_("Error reading string from inferior: %s"),
+ safe_strerror (err));
+ }
+ }
+
+ /* If the LENGTH is specified at -1, we want to return the string
+ length up to the terminating null character. If an actual length
+ was specified, we want to return the length of exactly what was
+ read. */
+ if (req_length == -1)
+ /* If the last character is null, subtract it from LENGTH. */
+ if (*length > 0
+ && extract_unsigned_integer (*buffer + *length - width, width,
+ byte_order) == 0)
+ *length -= width;
+
+ /* The read_string function will return the number of bytes read.
+ If length returned from read_string was > 0, return the number of
+ characters read by dividing the number of bytes by width. */
+ if (*length != 0)
+ *length = *length / width;
+
+ *char_type = element_type;
+
+ return;
+
+ error:
+ {
+ char *type_str;
+
+ type_str = type_to_string (type);
+ if (type_str)
+ {
+ make_cleanup (xfree, type_str);
+ error (_("Trying to read string with inappropriate type `%s'."),
+ type_str);
+ }
+ else
+ error (_("Trying to read string with inappropriate type."));
+ }
+}
+