| /* libnih |
| * |
| * string.c - useful string utility functions |
| * |
| * Copyright © 2006 Scott James Remnant <scott@netsplit.com>. |
| * |
| * This program 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 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <nih/macros.h> |
| #include <nih/alloc.h> |
| #include <nih/logging.h> |
| |
| #include "string.h" |
| |
| |
| /** |
| * nih_sprintf: |
| * @parent: parent block of allocation, |
| * @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 allocated |
| * block which will be used as the parent for this block. When @parent |
| * is freed, the returned string will be freed too. If you have clean-up |
| * that would need to be run, you can assign a destructor function using |
| * the #nih_alloc_set_destructor function. |
| * |
| * Returns: newly allocated string or %NULL. |
| **/ |
| char * |
| nih_sprintf (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 block of allocation, |
| * @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 allocated |
| * block which will be used as the parent for this block. When @parent |
| * is freed, the returned string will be freed too. If you have clean-up |
| * that would need to be run, you can assign a destructor function using |
| * the #nih_alloc_set_destructor function. |
| * |
| * Returns: newly allocated string or %NULL. |
| **/ |
| char * |
| nih_vsprintf (void *parent, |
| const char *format, |
| va_list args) |
| { |
| size_t len; |
| va_list args_copy; |
| char *str; |
| |
| nih_assert (format != NULL); |
| |
| va_copy (args_copy, args); |
| |
| len = vsnprintf (NULL, 0, format, args); |
| |
| str = nih_alloc (parent, len + 1); |
| if (! str) |
| return NULL; |
| |
| vsnprintf (str, len + 1, format, args_copy); |
| |
| return str; |
| } |
| |
| |
| /** |
| * nih_strdup: |
| * @parent: parent block of allocation, |
| * @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 allocated |
| * block which will be used as the parent for this block. When @parent |
| * is freed, the returned block will be freed too. If you have clean-up |
| * that would need to be run, you can assign a destructor function using |
| * the #nih_alloc_set_destructor function. |
| * |
| * Returns: duplicated string or %NULL if allocation fails. |
| **/ |
| char * |
| nih_strdup (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 block of allocation, |
| * @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 allocated |
| * block which will be used as the parent for this block. When @parent |
| * is freed, the returned block will be freed too. If you have clean-up |
| * that would need to be run, you can assign a destructor function using |
| * the #nih_alloc_set_destructor function. |
| * |
| * Returns: duplicated string or %NULL if allocation fails. |
| **/ |
| char * |
| nih_strndup (void *parent, |
| const char *str, |
| size_t len) |
| { |
| char *dup; |
| |
| nih_assert (str != NULL); |
| |
| dup = nih_alloc (parent, len + 1); |
| if (! str) |
| return NULL; |
| |
| memset (dup, '\0', len + 1); |
| strncpy (dup, str, len); |
| |
| return dup; |
| } |
| |
| |
| /** |
| * nih_str_split: |
| * @parent: parent of returned 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 and must NOT use #nih_strv_free. |
| * |
| * Returns: allocated array or %NULL if allocation fails. |
| **/ |
| char ** |
| nih_str_split (void *parent, |
| const char *str, |
| const char *delim, |
| int repeat) |
| { |
| char **array; |
| int i; |
| |
| nih_assert (str != NULL); |
| nih_assert (delim != NULL); |
| |
| i = 0; |
| array = nih_alloc (parent, sizeof (char *) * (i + 1)); |
| array[0] = NULL; |
| |
| while (*str) { |
| const char *ptr; |
| char **new_array; |
| |
| /* Skip initial delimiters */ |
| while (repeat && strchr (delim, *str)) |
| str++; |
| |
| /* Find the end of the token */ |
| ptr = str; |
| while (*str && (! strchr (delim, *str))) |
| str++; |
| |
| /* Increase the size of the array */ |
| new_array = nih_realloc (array, parent, |
| sizeof (char *) * (i + 2)); |
| if (! new_array) { |
| nih_free (array); |
| return NULL; |
| } |
| array = new_array; |
| |
| /* Fill in the new value */ |
| array[i++] = nih_strndup (array, ptr, str - ptr); |
| array[i] = NULL; |
| |
| /* Skip over the delimiter */ |
| if (*str) |
| str++; |
| } |
| |
| return array; |
| } |
| |
| /** |
| * nih_strv_free: |
| * @strv: array of strings: |
| * |
| * Free the given array of strings which should NOT have been allocated |
| * using #nih_alloc (as you could just free the parent array if you used |
| * that). |
| * |
| * The last member of the array should be NULL, and the array itself is |
| * not freed. |
| **/ |
| void |
| nih_strv_free (char **strv) |
| { |
| register char **s; |
| |
| for (s = strv; *s; s++) |
| free (*s); |
| } |
| |
| |
| /** |
| * nih_str_wrap: |
| * @parent: parent of returned 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. |
| * |
| * Returns: newly allocated string or %NULL if insufficient memory. |
| **/ |
| char * |
| nih_str_wrap (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; |
| } |