| /* libnih |
| * |
| * string.c - useful string utility functions |
| * |
| * Copyright © 2009 Scott James Remnant <scott@netsplit.com>. |
| * Copyright © 2009 Canonical Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| |
| #include <sys/ioctl.h> |
| |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <nih/macros.h> |
| #include <nih/alloc.h> |
| #include <nih/logging.h> |
| |
| #include "string.h" |
| |
| |
| /** |
| * nih_sprintf: |
| * @parent: parent object for new string, |
| * @format: format string. |
| * |
| * Writes a new string according to @format as sprintf(), except that the |
| * string is allocated using nih_alloc(). |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_sprintf (const void *parent, |
| const char *format, |
| ...) |
| { |
| char *str; |
| va_list args; |
| |
| nih_assert (format != NULL); |
| |
| va_start (args, format); |
| str = nih_vsprintf (parent, format, args); |
| va_end (args); |
| |
| return str; |
| } |
| |
| /** |
| * nih_vsprintf: |
| * @parent: parent object for new string, |
| * @format: format string, |
| * @args: arguments to format string. |
| * |
| * Writes a new string according to @format as vsprintf(), except that the |
| * string is allocated using nih_alloc(). |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_vsprintf (const void *parent, |
| const char *format, |
| va_list args) |
| { |
| ssize_t len; |
| va_list args_copy; |
| char *str; |
| |
| nih_assert (format != NULL); |
| |
| va_copy (args_copy, args); |
| len = vsnprintf (NULL, 0, format, args_copy); |
| va_end (args_copy); |
| |
| nih_assert (len >= 0); |
| |
| str = nih_alloc (parent, len + 1); |
| if (! str) |
| return NULL; |
| |
| va_copy (args_copy, args); |
| vsnprintf (str, len + 1, format, args_copy); |
| va_end (args_copy); |
| |
| return str; |
| } |
| |
| |
| /** |
| * nih_strdup: |
| * @parent: parent object for new string, |
| * @str: string to duplicate. |
| * |
| * Allocates enough memory to store a duplicate of @str and writes a |
| * copy of the string to it. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: duplicated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strdup (const void *parent, |
| const char *str) |
| { |
| size_t len; |
| |
| nih_assert (str != NULL); |
| |
| len = strlen (str); |
| return nih_strndup (parent, str, len); |
| } |
| |
| /** |
| * nih_strndup: |
| * @parent: parent object for new string, |
| * @str: string to duplicate, |
| * @len: length of string to duplicate. |
| * |
| * Allocates enough memory to store up to @len bytes of @str, or if @str |
| * is shorter, @len bytes. A copy of the string is written to this |
| * block with a NUL byte appended. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: duplicated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strndup (const void *parent, |
| const char *str, |
| size_t len) |
| { |
| char *dup; |
| |
| nih_assert (str != NULL); |
| |
| dup = nih_alloc (parent, len + 1); |
| if (! dup) |
| return NULL; |
| |
| memset (dup, '\0', len + 1); |
| strncpy (dup, str, len); |
| |
| return dup; |
| } |
| |
| |
| /** |
| * nih_strcat: |
| * @str: pointer to string to modify, |
| * @parent: parent object of new string, |
| * @src: string to append to @str. |
| * |
| * Modifies @str, concatenating the contents of @src to it. The new string |
| * is allocated using nih_alloc(), and @str will be updated to point to the |
| * new pointer; use the return value simply to check for success. |
| * |
| * If the string pointed to by @str is NULL, this is equivalent to |
| * nih_strdup() and if @parent is not NULL, it should be a pointer to another |
| * object which will be used as a parent for the returned string. When all |
| * parents of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * When the string pointed to by @str is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @str for style reasons. |
| * |
| * Returns: new string pointer or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strcat (char **str, |
| const void *parent, |
| const char *src) |
| { |
| nih_assert (str != NULL); |
| nih_assert (src != NULL); |
| |
| return nih_strncat (str, parent, src, strlen (src)); |
| } |
| |
| /** |
| * nih_strncat: |
| * @str: pointer to string to modify, |
| * @parent: parent object of new string, |
| * @src: string to append to @str, |
| * @len: length of @src. |
| * |
| * Modifies @str, concatenating up to @len characters of the contents of @src |
| * to it. The new string is allocated using nih_alloc(), and @str will be |
| * updated to point to the new pointer; use the return value simply to check |
| * for success. |
| * |
| * If the string pointed to by @str is NULL, this is equivalent to |
| * nih_strdup() and if @parent is not NULL, it should be a pointer to another |
| * object which will be used as a parent for the returned string. When all |
| * parents of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * When the string pointed to by @str is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @str for style reasons. |
| * |
| * Returns: new string pointer or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strncat (char **str, |
| const void *parent, |
| const char *src, |
| size_t len) |
| { |
| char *ret; |
| |
| nih_assert (str != NULL); |
| nih_assert (src != NULL); |
| |
| if (! *str) { |
| *str = nih_strndup (parent, src, len); |
| return *str; |
| } |
| |
| |
| ret = nih_realloc (*str, parent, strlen (*str) + len + 1); |
| if (! ret) |
| return NULL; |
| |
| *str = ret; |
| |
| strncat (*str, src, len); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * nih_strcat_sprintf: |
| * @str: pointer to string to modify, |
| * @parent: parent object of new string, |
| * @format: format string to append to @str. |
| * |
| * Modifies @str, concatenating according to @format as sprintf(). The new |
| * string is allocated using nih_alloc(), and @str will be updated to point |
| * to the new pointer; use the return value simply to check for success. |
| * |
| * If the string pointed to by @str is NULL, this is equivalent to |
| * nih_sprintf() and if @parent is not NULL, it should be a pointer to another |
| * object which will be used as a parent for the returned string. When all |
| * parents of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * When the string pointed to by @str is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @str for style reasons. |
| * |
| * Returns: new string pointer or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strcat_sprintf (char **str, |
| const void *parent, |
| const char *format, |
| ...) |
| { |
| char *ret; |
| va_list args; |
| |
| nih_assert (str != NULL); |
| nih_assert (format != NULL); |
| |
| va_start (args, format); |
| ret = nih_strcat_vsprintf (str, parent, format, args); |
| va_end (args); |
| |
| return ret; |
| } |
| |
| /** |
| * nih_strcat_vsprintf: |
| * @str: pointer to string to modify, |
| * @parent: parent object of new string, |
| * @format: format string to append to @str, |
| * @args: arguments to format string. |
| * |
| * Modifies @str, concatenating according to @format as vsprintf(). The new |
| * string is allocated using nih_alloc(), and @str will be updated to point |
| * to the new pointer; use the return value simply to check for success. |
| * |
| * If the string pointed to by @str is NULL, this is equivalent to |
| * nih_vsprintf() and if @parent is not NULL, it should be a pointer to another |
| * object which will be used as a parent for the returned string. When all |
| * parents of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * When the string pointed to by @str is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @str for style reasons. |
| * |
| * Returns: new string pointer or NULL if insufficient memory. |
| **/ |
| char * |
| nih_strcat_vsprintf (char **str, |
| const void *parent, |
| const char *format, |
| va_list args) |
| { |
| ssize_t len, str_len; |
| va_list args_copy; |
| char *ret; |
| |
| nih_assert (str != NULL); |
| nih_assert (format != NULL); |
| |
| str_len = *str ? strlen (*str) : 0; |
| |
| va_copy (args_copy, args); |
| len = vsnprintf (NULL, 0, format, args_copy); |
| va_end (args_copy); |
| |
| nih_assert (len >= 0); |
| |
| ret = nih_realloc (*str, parent, str_len + len + 1); |
| if (! ret) |
| return NULL; |
| |
| *str = ret; |
| |
| va_copy (args_copy, args); |
| vsnprintf (*str + str_len, len + 1, format, args_copy); |
| va_end (args_copy); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * nih_str_split: |
| * @parent: parent object of new array, |
| * @str: string to split, |
| * @delim: characters to split on, |
| * @repeat: allow repeated characters. |
| * |
| * Splits @str into an array of strings by separating on any character in |
| * @delim; if @repeat is true then sequences of @delim are ignored, otherwise |
| * they result in empty strings in the returned array. |
| * |
| * The last element in the array is always NULL. |
| * |
| * The individual strings are allocated using nih_alloc() so you may just use |
| * nih_free() on the returned array. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned array. When all parents |
| * of the returned string are freed, the returned array will also be |
| * freed. |
| * |
| * Returns: allocated array or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_split (const void *parent, |
| const char *str, |
| const char *delim, |
| int repeat) |
| { |
| char **array; |
| size_t len; |
| |
| nih_assert (str != NULL); |
| nih_assert (delim != NULL); |
| |
| len = 0; |
| array = nih_str_array_new (parent); |
| if (! array) |
| return NULL; |
| |
| while (*str) { |
| const char *ptr; |
| |
| /* Skip initial delimiters */ |
| while (repeat && strchr (delim, *str)) |
| str++; |
| |
| /* Find the end of the token */ |
| ptr = str; |
| while (*str && (! strchr (delim, *str))) |
| str++; |
| |
| if (! nih_str_array_addn (&array, parent, &len, |
| ptr, str - ptr)) { |
| nih_free (array); |
| return NULL; |
| } |
| |
| /* Skip over the delimiter */ |
| if (*str) |
| str++; |
| } |
| |
| return array; |
| } |
| |
| |
| /** |
| * nih_str_array_new: |
| * @parent: parent object of new array. |
| * |
| * Allocates a new NULL-terminated array of strings with zero elements; |
| * use nih_str_array_add() to append new strings to the array. Because |
| * each array element will be allocated using nih_alloc() as a child of |
| * the array itself, the entire array can be freed with nih_free(). |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned array. When all parents |
| * of the returned object are freed, the returned array will also be |
| * freed. |
| * |
| * Returns: newly allocated array or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_new (const void *parent) |
| { |
| char **array; |
| |
| array = nih_alloc (parent, sizeof (char *)); |
| if (! array) |
| return NULL; |
| |
| array[0] = NULL; |
| |
| return array; |
| } |
| |
| /** |
| * nih_str_array_add: |
| * @array: array of strings, |
| * @parent: parent object of new array, |
| * @len: length of @array, |
| * @str: string to add. |
| * |
| * Extend the NULL-terminated string @array (which has @len elements, |
| * excluding the final NULL element), appending a copy of @str to it. |
| * Both the array and the new string are allocated using nih_alloc(), |
| * |
| * @len will be updated to contain the new array length and @array will |
| * be updated to point to the new array pointer; use the return value |
| * simply to check for success. |
| * |
| * If you don't know or care about the length, @len may be set to NULL; |
| * this is less efficient as it necessates counting the length on each |
| * invocation. |
| * |
| * If the array pointed to by @array is NULL, the array will be allocated |
| * and @ptr the first element, and if @parent is not NULL, it should be a |
| * pointer to another object which will be used as a parent for the returned |
| * array. When all parents of the returned array are freed, the returned |
| * array will also be freed. |
| * |
| * When the array pointed to by @array is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @array for style reasons. |
| * |
| * Returns: new array pointer or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_add (char ***array, |
| const void *parent, |
| size_t *len, |
| const char *str) |
| { |
| nih_local char *new_str; |
| |
| nih_assert (array != NULL); |
| nih_assert (str != NULL); |
| |
| new_str = nih_strdup (NULL, str); |
| if (! new_str) |
| return NULL; |
| |
| return nih_str_array_addp (array, parent, len, new_str); |
| } |
| |
| /** |
| * nih_str_array_addn: |
| * @array: array of strings, |
| * @parent: parent object of new array, |
| * @len: length of @array, |
| * @str: string to add, |
| * @strlen: length of @str. |
| * |
| * Extend the NULL-terminated string @array (which has @len elements, |
| * excluding the final NULL element), appending a copy of the first |
| * @strlen bytes of @str to it. |
| * |
| * Both the array and the new string are allocated using nih_alloc(), |
| * |
| * @len will be updated to contain the new array length and @array will |
| * be updated to point to the new array pointer; use the return value |
| * simply to check for success. |
| * |
| * If you don't know or care about the length, @len may be set to NULL; |
| * this is less efficient as it necessates counting the length on each |
| * invocation. |
| * |
| * If the array pointed to by @array is NULL, the array will be allocated |
| * and @ptr the first element, and if @parent is not NULL, it should be a |
| * pointer to another object which will be used as a parent for the returned |
| * array. When all parents of the returned array are freed, the returned |
| * array will also be freed. |
| * |
| * When the array pointed to by @array is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @array for style reasons. |
| * |
| * Returns: new array pointer or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_addn (char ***array, |
| const void *parent, |
| size_t *len, |
| const char *str, |
| size_t strlen) |
| { |
| nih_local char *new_str; |
| |
| nih_assert (array != NULL); |
| nih_assert (str != NULL); |
| |
| new_str = nih_strndup (NULL, str, strlen); |
| if (! new_str) |
| return NULL; |
| |
| return nih_str_array_addp (array, parent, len, new_str); |
| } |
| |
| /** |
| * nih_str_array_addp: |
| * @array: array of strings, |
| * @parent: parent object of new array, |
| * @len: length of @array, |
| * @ptr: pointer to add. |
| * |
| * Extend the NULL-terminated string @array (which has @len elements, |
| * excluding the final NULL element), appending the nih_alloc() allocated |
| * object @ptr to it. |
| * |
| * The array is allocated using nih_alloc(), and @ptr will be referenced |
| * by the new array. After calling this function, you should never use |
| * nih_free() to free @ptr and instead use nih_unref() or nih_discard() |
| * if you no longer need to use it. |
| * |
| * @len will be updated to contain the new array length and @array will |
| * be updated to point to the new array pointer; use the return value |
| * simply to check for success. |
| * |
| * If you don't know or care about the length, @len may be set to NULL; |
| * this is less efficient as it necessates counting the length on each |
| * invocation. |
| * |
| * If the array pointed to by @array is NULL, the array will be allocated |
| * and @ptr the first element, and if @parent is not NULL, it should be a |
| * pointer to another object which will be used as a parent for the returned |
| * array. When all parents of the returned array are freed, the returned |
| * array will also be freed. |
| * |
| * When the array pointed to by @array is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @array for style reasons. |
| * |
| * Returns: new array pointer or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_addp (char ***array, |
| const void *parent, |
| size_t *len, |
| void *ptr) |
| { |
| char **new_array; |
| size_t c_len; |
| |
| nih_assert (array != NULL); |
| nih_assert (ptr != NULL); |
| |
| if (! len) { |
| len = &c_len; |
| c_len = 0; |
| |
| for (new_array = *array; new_array && *new_array; new_array++) |
| c_len++; |
| } |
| |
| new_array = nih_realloc (*array, parent, sizeof (char *) * (*len + 2)); |
| if (! new_array) |
| return NULL; |
| |
| *array = new_array; |
| |
| nih_ref (ptr, *array); |
| |
| (*array)[(*len)++] = ptr; |
| (*array)[*len] = NULL; |
| |
| return *array; |
| } |
| |
| /** |
| * nih_str_array_copy: |
| * @parent: parent object of new array. |
| * @len: length of new array, |
| * @array: array of strings to copy. |
| * |
| * Allocates a new NULL-terminated array of strings with elements copied |
| * from the existing @array given. |
| * |
| * Because each array element will be allocated using nih_alloc() as a child |
| * of the array itself, the entire array can be freed with nih_free(). |
| * This will not affect the array copied. |
| * |
| * @len will be updated to contain the new array length. If you don't care |
| * about the length, @len may be set to NULL; this is less efficient as it |
| * necessates counting the length on each future add operation. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned array. When all parents |
| * of the returned array are freed, the returned array will also be |
| * freed. |
| * |
| * Returns: newly allocated array or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_copy (const void *parent, |
| size_t *len, |
| char * const *array) |
| { |
| char **new_array; |
| |
| nih_assert (array != NULL); |
| |
| new_array = nih_str_array_new (parent); |
| if (! new_array) |
| return NULL; |
| |
| if (! nih_str_array_append (&new_array, parent, len, array)) { |
| nih_free (new_array); |
| return NULL; |
| } |
| |
| return new_array; |
| } |
| |
| /** |
| * nih_str_array_append: |
| * @array: array of strings, |
| * @parent: parent object of new array, |
| * @len: length of @array, |
| * @args: array of strings to add. |
| * |
| * Extend the NULL-terminated string @array (which has @len elements, |
| * excluding the final NULL element), appending a copy of each element in |
| * the additional NULL-terminated string array @args to it. |
| * Both the array and the new strings are allocated using nih_alloc(), |
| * |
| * @len will be updated to contain the new array length and @array will |
| * be updated to point to the new array pointer; use the return value |
| * simply to check for success. |
| * |
| * If you don't know or care about the length, @len may be set to NULL; |
| * this is less efficient as it necessates counting the length on each |
| * operation. |
| * |
| * If the array pointed to by @array is NULL, this is equivalent to |
| * nih_str_array_copy() and if @parent is not NULL, it should be a pointer to |
| * another object which will be used as a parent for the returned array. |
| * When all parents of the returned array are freed, the returned array will |
| * also be freed. |
| * |
| * When the array pointed to by @array is not NULL, @parent is ignored; |
| * though it usual to pass a parent of @array for style reasons. |
| * |
| * Returns: new array pointer or NULL if insufficient memory. |
| **/ |
| char ** |
| nih_str_array_append (char ***array, |
| const void *parent, |
| size_t *len, |
| char * const *args) |
| { |
| size_t c_len, o_len; |
| int free_on_error = FALSE; |
| char * const *arg; |
| |
| nih_assert (array != NULL); |
| nih_assert (args != NULL); |
| |
| if (! *array) |
| free_on_error = TRUE; |
| |
| if (! len) { |
| c_len = 0; |
| |
| for (arg = *array; arg && *arg; arg++) |
| c_len++; |
| } else { |
| c_len = *len; |
| } |
| |
| o_len = c_len; |
| |
| for (arg = args; *arg; arg++) { |
| if (! nih_str_array_add (array, parent, &c_len, *arg)) { |
| if (*array) { |
| for (; c_len > o_len; c_len--) |
| nih_free ((*array)[c_len - 1]); |
| |
| (*array)[o_len] = NULL; |
| |
| if (free_on_error) { |
| nih_free (*array); |
| *array = NULL; |
| } |
| } |
| |
| return NULL; |
| } |
| } |
| |
| if (len) |
| *len = c_len; |
| |
| return *array; |
| } |
| |
| |
| /** |
| * nih_str_wrap: |
| * @parent: parent object of new string, |
| * @str: string to be wrapped, |
| * @len: length of line to fit into, |
| * @first_indent: indent for first line, |
| * @indent: indent for subsequent lines. |
| * |
| * Returns a newly allocated copy of @str with newlines inserted so no |
| * line is longer than @len characters (not including the newline). Where |
| * possible, newlines replace existing whitespace characters so that words |
| * are not broken. |
| * |
| * The first line may be indented by an extra @first_indent characters, and |
| * subsequent lines may be intended by an extra @indent characters. These |
| * are added to the string as whitespace characters. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_str_wrap (const void *parent, |
| const char *str, |
| size_t len, |
| size_t first_indent, |
| size_t indent) |
| { |
| char *txt; |
| size_t txtlen, col, ls, i; |
| |
| nih_assert (str != NULL); |
| nih_assert (len > 0); |
| |
| txtlen = first_indent + strlen (str); |
| txt = nih_alloc (parent, txtlen + 1); |
| if (! txt) |
| return NULL; |
| |
| memset (txt, ' ', first_indent); |
| memcpy (txt + first_indent, str, strlen (str) + 1); |
| |
| col = ls = 0; |
| for (i = 0; i < txtlen; i++) { |
| int nl = 0; |
| |
| if (strchr (" \t\r", txt[i])) { |
| /* Character is whitespace; convert to an ordinary |
| * space and remember the position for next time. |
| */ |
| txt[i] = ' '; |
| ls = i; |
| |
| /* If this doesn't go over the line length, |
| * continue to the next character |
| */ |
| if (++col <= len) |
| continue; |
| } else if (txt[i] != '\n') { |
| /* Character is part of a word. If this doesn't go |
| * over the line length, continue to the next |
| * character |
| */ |
| if (++col <= len) |
| continue; |
| |
| /* Filled a line; if we marked a whitespace character |
| * on this line, go back to that, otherwise we'll |
| * need to add a newline to the string after this |
| * character |
| */ |
| if (ls) { |
| i = ls; |
| } else { |
| nl = 1; |
| } |
| } |
| |
| /* We need to insert a line break at this position, and |
| * any indent that goes along with it |
| */ |
| if (indent | nl) { |
| char *new_txt; |
| |
| /* Need to increase the size of the string in memory */ |
| new_txt = nih_realloc (txt, parent, |
| txtlen + indent + nl + 1); |
| if (! new_txt) { |
| nih_free (txt); |
| return NULL; |
| } |
| txt = new_txt; |
| |
| /* Move up the existing characters, then replace |
| * the gap with the indent |
| */ |
| memmove (txt + i + indent + 1, txt + i + 1 - nl, |
| txtlen - i + nl); |
| memset (txt + i + 1, ' ', indent); |
| txtlen += indent + nl; |
| } |
| |
| /* Replace the current character with a newline */ |
| txt[i] = '\n'; |
| |
| /* Reset the current column and last seen whitespace index; |
| * make sure we skip any indent as it's whitespace that doesn't |
| * count |
| */ |
| i += indent; |
| col = indent; |
| ls = 0; |
| } |
| |
| return txt; |
| } |
| |
| /** |
| * nih_str_screen_width: |
| * |
| * Checks the COLUMNS environment variable, standard output if it is a |
| * terminal or defaults to 80 characters. |
| * |
| * Returns: the width of the screen. |
| **/ |
| size_t |
| nih_str_screen_width (void) |
| { |
| char *columns; |
| size_t len = 0; |
| |
| /* Look at the columns environment variable */ |
| columns = getenv ("COLUMNS"); |
| if ((! len) && columns) { |
| char *endptr; |
| |
| len = strtoul (columns, &endptr, 10); |
| if (*endptr) |
| len = 0; |
| } |
| |
| /* Check whether standard output is a tty */ |
| if ((! len) && isatty (STDOUT_FILENO)) { |
| struct winsize winsize; |
| |
| if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &winsize) == 0) |
| len = winsize.ws_col; |
| } |
| |
| /* Fallback to 80 columns */ |
| if (! len) |
| len = 80; |
| |
| return len; |
| } |
| |
| /** |
| * nih_str_screen_wrap: |
| * @parent: parent object of new string, |
| * @str: string to be wrapped, |
| * @first_indent: indent for first line, |
| * @indent: indent for subsequent lines. |
| * |
| * Returns a newly allocated copy of @str with newlines inserted so no |
| * line is wider than the screen (not including the newline). Where |
| * possible, newlines replace existing whitespace characters so that words |
| * are not broken. |
| * |
| * If standard output is not a terminal, then 80 characters is assumed. |
| * The width can be overriden with the COLUMNS environment variable. |
| * |
| * The first line may be indented by an extra @first_indent characters, and |
| * subsequent lines may be intended by an extra @indent characters. These |
| * are added to the string as whitespace characters. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned string. When all parents |
| * of the returned string are freed, the returned string will also be |
| * freed. |
| * |
| * Returns: newly allocated string or NULL if insufficient memory. |
| **/ |
| char * |
| nih_str_screen_wrap (const void *parent, |
| const char *str, |
| size_t first_indent, |
| size_t indent) |
| { |
| size_t len; |
| |
| nih_assert (str != NULL); |
| |
| len = nih_str_screen_width () - 1; |
| |
| return nih_str_wrap (parent, str, len, first_indent, indent); |
| } |