/* $Id$ */ /* Copyright (c) 2005-2021 Pierre Pronchery */ /* This file is part of DeforaOS System libSystem */ /* All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "System/error.h" #include "System/object.h" #include "System/string.h" /* String */ /* public */ /* string_new */ String * string_new(String const * string) { String * ret = NULL; if(string == NULL) { error_set_code(-EINVAL, "%s", strerror(EINVAL)); return NULL; } if(string_set(&ret, string) != 0) return NULL; return ret; } /* string_new_append */ String * string_new_append(String const * string, ...) { String * ret; va_list ap; va_start(ap, string); ret = string_new_appendv(string, ap); va_end(ap); return ret; } /* string_new_appendv */ String * string_new_appendv(String const * string, va_list ap) { String * ret = NULL; if(string == NULL) return string_new(""); ret = string_new(string); for(string = va_arg(ap, String *); string != NULL; string = va_arg(ap, String *)) if(string_append(&ret, string) != 0) { string_delete(ret); ret = NULL; break; } return ret; } /* string_new_format */ String * string_new_format(String const * format, ...) { String * ret; va_list ap; va_start(ap, format); ret = string_new_formatv(format, ap); va_end(ap); return ret; } /* string_new_formatv */ String * string_new_formatv(String const * format, va_list ap) { String * ret; va_list v; int len; size_t s; if(format == NULL) { error_set_code(-EINVAL, "%s", strerror(EINVAL)); return NULL; } va_copy(v, ap); len = vsnprintf(NULL, 0, format, v); va_end(v); if(len < 0) { error_set_code(-errno, "%s", strerror(errno)); return NULL; } s = (size_t)len + 1; if((ret = (String *)object_new(s)) == NULL) return NULL; if(vsnprintf(ret, s, format, ap) != len) { error_set_code(-errno, "%s", strerror(errno)); object_delete(ret); ret = NULL; } return ret; } /* string_new_length */ String * string_new_length(String const * string, size_t length) { String * ret; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", %zu)\n", __func__, string, length); #endif if(length + 1 == 0) { error_set_code(-ERANGE, "%s", strerror(ERANGE)); return NULL; } if((ret = (String *)object_new(length + 1)) == NULL) return NULL; snprintf(ret, length + 1, "%s", (string != NULL) ? string : ""); return ret; } /* string_new_replace */ String * string_new_replace(String const * string, String const * what, String const * by) { String * ret; if((ret = string_new(string)) == NULL) return NULL; if(string_replace(&ret, what, by) != 0) { string_delete(ret); return NULL; } return ret; } /* string_delete */ void string_delete(String * string) { object_delete(string); } /* accessors */ /* string_get */ # undef string_get String const * string_get(String const * string) { return string; } # define string_get(a) (a) /* string_get_length */ size_t string_get_length(String const * string) { size_t length; for(length = 0; string[length] != '\0'; length++); return length; } /* string_get_size */ size_t string_get_size(String const * string) { return string_get_length(string) + 1; } /* string_set */ int string_set(String ** string, String const * string2) { size_t len = string_get_length(string2); if(object_resize((Object **)string, len + 1) != 0) return 1; strcpy(*string, string2); return 0; } /* useful */ /* string_append */ int string_append(String ** string, String const * append) { size_t slength = (*string != NULL) ? string_get_length(*string) : 0; size_t alength; if(append == NULL) return error_set_code(-EINVAL, "%s", strerror(EINVAL)); if((alength = string_get_length(append)) == 0) return 0; if(object_resize((Object**)string, slength + alength + 1) != 0) return 1; strcpy(*string + slength, append); return 0; } /* string_append_format */ int string_append_format(String ** string, String const * format, ...) { int ret; va_list ap; va_start(ap, format); ret = string_append_formatv(string, format, ap); va_end(ap); return ret; } /* string_append_formatv */ int string_append_formatv(String ** string, String const * format, va_list ap) { va_list v; int alen; size_t len; size_t s; if(format == NULL) return error_set_code(-EINVAL, "%s", strerror(EINVAL)); va_copy(v, ap); alen = vsnprintf(NULL, 0, format, v); va_end(v); if(alen < 0) return error_set_code(-errno, "%s", strerror(errno)); len = string_get_length(*string); s = (size_t)alen + 1; if(object_resize((Object **)string, len + s) != 0) return -1; if(vsnprintf(&(*string)[len], s, format, ap) != alen) { error_set_code(-errno, "%s", strerror(errno)); return -1; } return 0; } /* string_clear */ void string_clear(String * string) { String * s; for(s = string; *s != '\0'; s++) *s = '\0'; } /* string_compare */ int string_compare(String const * string, String const * string2) { int ret; unsigned char const * u1; unsigned char const * u2; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%s, %s)\n", __func__, string, string2); #endif u1 = (unsigned char const *)string; u2 = (unsigned char const *)string2; while(*u1 && *u2 && *u1 == *u2) { u1++; u2++; } ret = *u1 - *u2; return ret; } /* string_compare_length */ int string_compare_length(String const * string, String const * string2, size_t length) { int ret; unsigned char const * u1; unsigned char const * u2; if(length == 0) return 0; u1 = (unsigned char const *)string; u2 = (unsigned char const *)string2; while(--length && *u1 && *u2 && *u1 == *u2) { u1++; u2++; } ret = *u1 - *u2; return ret; } /* string_explode */ static void _explose_foreach_delete(ArrayData * value, void * data); StringArray * string_explode(String const * string, String const * separator) { StringArray * ret; String * p; /* temporary pointer */ size_t i; /* current position */ String const * s; /* &string[i] */ ssize_t j; /* position of the next separator */ ssize_t l; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\")\n", __func__, string, separator); #endif if((ret = stringarray_new()) == NULL) return NULL; if(separator == NULL || (l = string_get_length(separator)) == 0) { error_set_code(-EINVAL, "%s", strerror(EINVAL)); array_delete(ret); return NULL; } for(i = 0;; i += j + l) { s = &string[i]; j = string_index(s, separator); #ifdef DEBUG fprintf(stderr, "DEBUG: %s(): i=%zu, j=%zd\n", __func__, i, j); #endif if(j < 0) { if((p = string_new(s)) == NULL || array_append(ret, p) != 0) { string_delete(p); break; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s(): \"%s\"\n", __func__, ret[ret_cnt - 1]); #endif return ret; } if((p = string_new_length(s, j)) == NULL || array_append(ret, p) != 0) { string_delete(p); break; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s(): \"%s\"\n", __func__, ret[ret_cnt - 1]); #endif } /* free everything */ array_foreach(ret, _explose_foreach_delete, NULL); array_delete(ret); return NULL; } static void _explose_foreach_delete(ArrayData * value, void * data) { String * s = (String *)value; (void) data; string_delete(s); } /* string_find */ String * string_find(String const * string, String const * key) { ssize_t i; if((i = string_index(string, key)) < 0) return NULL; return (String *)&string[i]; /* XXX */ } /* string_index */ ssize_t string_index(String const * string, String const * key) { size_t len; size_t keylen; size_t i; len = string_get_length(string); if((keylen = string_get_length(key)) == 0) return len; if(keylen > len) return -1; for(i = 0; i <= len - keylen; i++) if(string_compare_length(&string[i], key, keylen) == 0) return i; return -1; } /* string_ltrim */ size_t string_ltrim(String * string, String const * which) { size_t i; size_t j; for(i = 0; string[i] != '\0'; i++) if(which == NULL) { if(!isspace((unsigned char)string[i])) break; } else { for(j = 0; which[j] != '\0' && string[i] != which[j]; j++); if(which[j] == '\0') break; } for(j = i; string[j] != '\0'; j++) string[j - i] = string[j]; string[j - i] = '\0'; return i; } /* string_replace */ int string_replace(String ** string, String const * what, String const * by) { String * ret = NULL; String const * p; size_t len = string_get_length(what); ssize_t index; String * q; for(p = *string; (index = string_index(p, what)) >= 0; p += index + len) { if((q = string_new_length(p, index)) == NULL || string_append(&ret, q) != 0 || string_append(&ret, by) != 0) { string_delete(q); string_delete(ret); return -1; } string_delete(q); } if(ret != NULL) { if(string_append(&ret, p) != 0) { string_delete(ret); return -1; } string_delete(*string); *string = ret; } return 0; } /* string_rindex */ ssize_t string_rindex(String const * string, String const * key) { size_t len; size_t keylen; ssize_t i; len = string_get_length(string); if((keylen = string_get_length(key)) == 0) return len; if(keylen > len) return -1; for(i = len - keylen; i >= 0; i--) if(string_compare_length(&string[i], key, keylen) == 0) return i; return -1; } /* string_rtrim */ size_t string_rtrim(String * string, String const * which) { size_t ret = 0; size_t i; size_t j; for(i = string_get_length(string); i > 0; i--) if(which == NULL) { if(!isspace((unsigned char)string[i - 1])) return ret; string[i - 1] = '\0'; ret++; } else { for(j = 0; which[j] != '\0'; j++) if(string[i - 1] == which[j]) { string[i - 1] = '\0'; ret++; break; } if(which[j] == '\0') break; } return ret; } /* string_tolower */ void string_tolower(String * string) { size_t i; for(i = string_get_length(string); i > 0; i--) string[i - 1] = tolower((unsigned char)string[i - 1]); } /* string_toupper */ void string_toupper(String * string) { size_t i; for(i = string_get_length(string); i > 0; i--) string[i - 1] = toupper((unsigned char)string[i - 1]); } /* string_trim */ size_t string_trim(String * string, String const * which) { return string_ltrim(string, which) + string_rtrim(string, which); }