629 lines
9.9 KiB
C
629 lines
9.9 KiB
C
/* str.c - Common string-related functions
|
|
*
|
|
* Copyright (C)
|
|
* 2000 Russell Kroll <rkroll@exploits.org>
|
|
* 2015 Daniele Pezzini <hyouko@gmail.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "config.h" /* must be first */
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h> /* for strdup() and many others */
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h> /* for strncasecmp() and strcasecmp() */
|
|
#endif
|
|
|
|
#include "nut_stdint.h"
|
|
#include "str.h"
|
|
|
|
char *str_trim(char *string, const char character)
|
|
{
|
|
return str_rtrim(str_ltrim(string, character), character);
|
|
}
|
|
|
|
char *str_trim_m(char *string, const char *characters)
|
|
{
|
|
return str_rtrim_m(str_ltrim_m(string, characters), characters);
|
|
}
|
|
|
|
char *str_ltrim(char *string, const char character)
|
|
{
|
|
char characters[2] = { character, '\0' };
|
|
|
|
return str_ltrim_m(string, characters);
|
|
}
|
|
|
|
char *str_ltrim_m(char *string, const char *characters)
|
|
{
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0' ||
|
|
characters == NULL ||
|
|
*characters == '\0'
|
|
)
|
|
return string;
|
|
|
|
while (
|
|
*string != '\0' &&
|
|
strchr(characters, *string) != NULL
|
|
)
|
|
memmove(string, string + 1, strlen(string));
|
|
|
|
return string;
|
|
}
|
|
|
|
char *str_rtrim(char *string, const char character)
|
|
{
|
|
char characters[2] = { character, '\0' };
|
|
|
|
return str_rtrim_m(string, characters);
|
|
}
|
|
|
|
char *str_rtrim_m(char *string, const char *characters)
|
|
{
|
|
char *ptr;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0' ||
|
|
characters == NULL ||
|
|
*characters == '\0'
|
|
)
|
|
return string;
|
|
|
|
ptr = &string[strlen(string) - 1];
|
|
|
|
while (
|
|
ptr >= string &&
|
|
strchr(characters, *ptr) != NULL
|
|
)
|
|
*ptr-- = '\0';
|
|
|
|
return string;
|
|
}
|
|
|
|
char *str_trim_space(char *string)
|
|
{
|
|
return str_rtrim_space(str_ltrim_space(string));
|
|
}
|
|
|
|
char *str_ltrim_space(char *string)
|
|
{
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0'
|
|
)
|
|
return string;
|
|
|
|
while (
|
|
*string != '\0' &&
|
|
isspace((size_t)*string)
|
|
)
|
|
memmove(string, string + 1, strlen(string));
|
|
|
|
return string;
|
|
}
|
|
|
|
char *str_rtrim_space(char *string)
|
|
{
|
|
char *ptr;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0'
|
|
)
|
|
return string;
|
|
|
|
ptr = &string[strlen(string) - 1];
|
|
|
|
while (
|
|
ptr >= string &&
|
|
isspace((size_t)*ptr)
|
|
)
|
|
*ptr-- = '\0';
|
|
|
|
return string;
|
|
}
|
|
|
|
int str_is_short(const char *string, const int base)
|
|
{
|
|
short number;
|
|
|
|
return str_to_short(string, &number, base);
|
|
}
|
|
|
|
int str_is_short_strict(const char *string, const int base)
|
|
{
|
|
short number;
|
|
|
|
return str_to_short_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_ushort(const char *string, const int base)
|
|
{
|
|
unsigned short number;
|
|
|
|
return str_to_ushort(string, &number, base);
|
|
}
|
|
|
|
int str_is_ushort_strict(const char *string, const int base)
|
|
{
|
|
unsigned short number;
|
|
|
|
return str_to_ushort_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_int(const char *string, const int base)
|
|
{
|
|
int number;
|
|
|
|
return str_to_int(string, &number, base);
|
|
}
|
|
|
|
int str_is_int_strict(const char *string, const int base)
|
|
{
|
|
int number;
|
|
|
|
return str_to_int_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_uint(const char *string, const int base)
|
|
{
|
|
unsigned int number;
|
|
|
|
return str_to_uint(string, &number, base);
|
|
}
|
|
|
|
int str_is_uint_strict(const char *string, const int base)
|
|
{
|
|
unsigned int number;
|
|
|
|
return str_to_uint_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_long(const char *string, const int base)
|
|
{
|
|
long number;
|
|
|
|
return str_to_long(string, &number, base);
|
|
}
|
|
|
|
int str_is_long_strict(const char *string, const int base)
|
|
{
|
|
long number;
|
|
|
|
return str_to_long_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_ulong(const char *string, const int base)
|
|
{
|
|
unsigned long number;
|
|
|
|
return str_to_ulong(string, &number, base);
|
|
}
|
|
|
|
int str_is_ulong_strict(const char *string, const int base)
|
|
{
|
|
unsigned long number;
|
|
|
|
return str_to_ulong_strict(string, &number, base);
|
|
}
|
|
|
|
int str_is_double(const char *string, const int base)
|
|
{
|
|
double number;
|
|
|
|
return str_to_double(string, &number, base);
|
|
}
|
|
|
|
int str_is_double_strict(const char *string, const int base)
|
|
{
|
|
double number;
|
|
|
|
return str_to_double_strict(string, &number, base);
|
|
}
|
|
|
|
int str_to_short(const char *string, short *number, const int base)
|
|
{
|
|
long num;
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_long(string, &num, base))
|
|
return 0;
|
|
|
|
if (
|
|
num < SHRT_MIN ||
|
|
num > SHRT_MAX
|
|
) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (short)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_short_strict(const char *string, short *number, const int base)
|
|
{
|
|
long num;
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_long_strict(string, &num, base))
|
|
return 0;
|
|
|
|
if (
|
|
num < SHRT_MIN ||
|
|
num > SHRT_MAX
|
|
) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (short)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_ushort(const char *string, unsigned short *number, const int base)
|
|
{
|
|
unsigned long num;
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_ulong(string, &num, base))
|
|
return 0;
|
|
|
|
if (num > USHRT_MAX) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (unsigned short)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_ushort_strict(const char *string, unsigned short *number, const int base)
|
|
{
|
|
unsigned long num;
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_ulong_strict(string, &num, base))
|
|
return 0;
|
|
|
|
if (num > USHRT_MAX) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (unsigned short)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_int(const char *string, int *number, const int base)
|
|
{
|
|
long num; /* long >= int, make sure we fit well */
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_long(string, &num, base))
|
|
return 0;
|
|
|
|
if (
|
|
num < INT_MIN ||
|
|
num > INT_MAX
|
|
) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (int)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_int_strict(const char *string, int *number, const int base)
|
|
{
|
|
long num; /* long >= int, make sure we fit well */
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_long_strict(string, &num, base))
|
|
return 0;
|
|
|
|
if (
|
|
num < INT_MIN ||
|
|
num > INT_MAX
|
|
) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (int)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_uint(const char *string, unsigned int *number, const int base)
|
|
{
|
|
unsigned long num; /* long >= int, make sure we fit well */
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_ulong(string, &num, base))
|
|
return 0;
|
|
|
|
if (num > UINT_MAX) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (unsigned int)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_uint_strict(const char *string, unsigned int *number, const int base)
|
|
{
|
|
unsigned long num; /* long >= int, make sure we fit well */
|
|
|
|
*number = 0;
|
|
|
|
if (!str_to_ulong_strict(string, &num, base))
|
|
return 0;
|
|
|
|
if (num > UINT_MAX) {
|
|
errno = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
*number = (unsigned int)num;
|
|
return 1;
|
|
}
|
|
|
|
int str_to_long(const char *string, long *number, const int base)
|
|
{
|
|
char *str;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0'
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
str = strdup(string);
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
str_trim_space(str);
|
|
|
|
if (!str_to_long_strict(str, number, base)) {
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
free(str);
|
|
return 1;
|
|
}
|
|
|
|
int str_to_long_strict(const char *string, long *number, const int base)
|
|
{
|
|
char *ptr = NULL;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0' ||
|
|
isspace((size_t)*string)
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
errno = 0;
|
|
*number = strtol(string, &ptr, base);
|
|
|
|
if (
|
|
errno == EINVAL ||
|
|
*ptr != '\0'
|
|
) {
|
|
*number = 0;
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
if (errno == ERANGE) {
|
|
*number = 0;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int str_to_ulong(const char *string, unsigned long *number, const int base)
|
|
{
|
|
char *str;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0'
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
str = strdup(string);
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
str_trim_space(str);
|
|
|
|
if (!str_to_ulong_strict(str, number, base)) {
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
free(str);
|
|
return 1;
|
|
}
|
|
|
|
int str_to_ulong_strict(const char *string, unsigned long *number, const int base)
|
|
{
|
|
char *ptr = NULL;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0' ||
|
|
*string == '+' ||
|
|
*string == '-' ||
|
|
isspace((size_t)*string)
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
errno = 0;
|
|
*number = strtoul(string, &ptr, base);
|
|
|
|
if (
|
|
errno == EINVAL ||
|
|
*ptr != '\0'
|
|
) {
|
|
*number = 0;
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
if (errno == ERANGE) {
|
|
*number = 0;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int str_to_double(const char *string, double *number, const int base)
|
|
{
|
|
char *str;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0'
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
str = strdup(string);
|
|
if (str == NULL)
|
|
return 0;
|
|
|
|
str_trim_space(str);
|
|
|
|
if (!str_to_double_strict(str, number, base)) {
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
free(str);
|
|
return 1;
|
|
}
|
|
|
|
int str_to_double_strict(const char *string, double *number, const int base)
|
|
{
|
|
char *ptr = NULL;
|
|
|
|
*number = 0;
|
|
|
|
if (
|
|
string == NULL ||
|
|
*string == '\0' ||
|
|
isspace((size_t)*string)
|
|
) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
switch (base)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 10:
|
|
if (strlen(string) != strspn(string, "-+.0123456789Ee")) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
break;
|
|
case 16:
|
|
if (strlen(string) != strspn(string, "-+.0123456789ABCDEFabcdefXxPp")) {
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
errno = 0;
|
|
*number = strtod(string, &ptr);
|
|
|
|
if (
|
|
errno == EINVAL ||
|
|
*ptr != '\0'
|
|
) {
|
|
*number = 0;
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
if (errno == ERANGE) {
|
|
*number = 0;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int str_ends_with(const char *s, const char *suff) {
|
|
size_t slen;
|
|
size_t sufflen;
|
|
|
|
if (!s) return 0; /* null string does not end with anything */
|
|
if (!suff) return 1; /* null suffix tails anything */
|
|
|
|
slen = strlen(s);
|
|
sufflen = strlen(suff);
|
|
|
|
return (slen >= sufflen) && (!memcmp(s + slen - sufflen, suff, sufflen));
|
|
}
|