The goal is to solve confusion of the "All rights reserved" for licensing
code under an open-source license.
The phrase "All rights reserved" comes from a historical convention that
required this phrase for the copyright protection to apply. This convention
is no longer relevant.
However, even though the phrase has no meaning in establishing the copyright
it has not lost meaning in terms of licensing.
This change makes it so code under the Blender Foundation copyright does
not use "all rights reserved". This is also how the GPL license itself
states how to apply it to the source code:
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software ...
This change does not change copyright notice in cases when the copyright
is dual (BF and an author), or just an author of the code. It also does
mot change copyright which is inherited from NaN Holding BV as it needs
some further investigation about what is the proper way to handle it.
453 lines
11 KiB
C
453 lines
11 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2017 Blender Foundation */
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_string.h"
|
|
#include "BLI_string_utf8.h"
|
|
#include "BLI_string_utils.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_listBase.h"
|
|
|
|
#ifdef __GNUC__
|
|
# pragma GCC diagnostic error "-Wsign-conversion"
|
|
#endif
|
|
|
|
size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim)
|
|
{
|
|
const size_t name_len = strlen(name);
|
|
|
|
*nr = 0;
|
|
memcpy(left, name, (name_len + 1) * sizeof(char));
|
|
|
|
/* name doesn't end with a delimiter "foo." */
|
|
if ((name_len > 1 && name[name_len - 1] == delim) == 0) {
|
|
size_t a = name_len;
|
|
while (a--) {
|
|
if (name[a] == delim) {
|
|
left[a] = '\0'; /* truncate left part here */
|
|
*nr = atol(name + a + 1);
|
|
/* casting down to an int, can overflow for large numbers */
|
|
if (*nr < 0) {
|
|
*nr = 0;
|
|
}
|
|
return a;
|
|
}
|
|
if (isdigit(name[a]) == 0) {
|
|
/* non-numeric suffix - give up */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return name_len;
|
|
}
|
|
|
|
bool BLI_string_is_decimal(const char *string)
|
|
{
|
|
if (*string == '\0') {
|
|
return false;
|
|
}
|
|
|
|
/* Keep iterating over the string until a non-digit is found. */
|
|
while (isdigit(*string)) {
|
|
string++;
|
|
}
|
|
|
|
/* If the non-digit we found is the terminating \0, everything was digits. */
|
|
return *string == '\0';
|
|
}
|
|
|
|
static bool is_char_sep(const char c)
|
|
{
|
|
return ELEM(c, '.', ' ', '-', '_');
|
|
}
|
|
|
|
void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len)
|
|
{
|
|
size_t len = BLI_strnlen(string, str_len);
|
|
size_t i;
|
|
|
|
r_body[0] = r_suf[0] = '\0';
|
|
|
|
for (i = len; i > 0; i--) {
|
|
if (is_char_sep(string[i])) {
|
|
BLI_strncpy(r_body, string, i + 1);
|
|
BLI_strncpy(r_suf, string + i, (len + 1) - i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
memcpy(r_body, string, len + 1);
|
|
}
|
|
|
|
void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len)
|
|
{
|
|
size_t len = BLI_strnlen(string, str_len);
|
|
size_t i;
|
|
|
|
r_body[0] = r_pre[0] = '\0';
|
|
|
|
for (i = 1; i < len; i++) {
|
|
if (is_char_sep(string[i])) {
|
|
i++;
|
|
BLI_strncpy(r_pre, string, i + 1);
|
|
BLI_strncpy(r_body, string + i, (len + 1) - i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
BLI_strncpy(r_body, string, len);
|
|
}
|
|
|
|
size_t BLI_string_flip_side_name(char *r_name,
|
|
const char *from_name,
|
|
const bool strip_number,
|
|
const size_t name_len)
|
|
{
|
|
size_t len;
|
|
char *prefix = alloca(name_len); /* The part before the facing */
|
|
char *suffix = alloca(name_len); /* The part after the facing */
|
|
char *replace = alloca(name_len); /* The replacement string */
|
|
char *number = alloca(name_len); /* The number extension string */
|
|
char *index = NULL;
|
|
bool is_set = false;
|
|
|
|
*prefix = *suffix = *replace = *number = '\0';
|
|
|
|
/* always copy the name, since this can be called with an uninitialized string */
|
|
len = BLI_strncpy_rlen(r_name, from_name, name_len);
|
|
if (len < 3) {
|
|
/* we don't do names like .R or .L */
|
|
return len;
|
|
}
|
|
|
|
/* We first check the case with a .### extension, let's find the last period */
|
|
if (isdigit(r_name[len - 1])) {
|
|
index = strrchr(r_name, '.'); /* Last occurrence. */
|
|
if (index && isdigit(index[1])) { /* Doesn't handle case `bone.1abc2` correct..., whatever! */
|
|
if (strip_number == false) {
|
|
BLI_strncpy(number, index, name_len);
|
|
}
|
|
*index = 0;
|
|
len = BLI_strnlen(r_name, name_len);
|
|
}
|
|
}
|
|
|
|
BLI_strncpy(prefix, r_name, name_len);
|
|
|
|
/* First case; separator (`.` or `_`) with extensions in `r R l L`. */
|
|
if ((len > 1) && is_char_sep(r_name[len - 2])) {
|
|
is_set = true;
|
|
switch (r_name[len - 1]) {
|
|
case 'l':
|
|
prefix[len - 1] = 0;
|
|
strcpy(replace, "r");
|
|
break;
|
|
case 'r':
|
|
prefix[len - 1] = 0;
|
|
strcpy(replace, "l");
|
|
break;
|
|
case 'L':
|
|
prefix[len - 1] = 0;
|
|
strcpy(replace, "R");
|
|
break;
|
|
case 'R':
|
|
prefix[len - 1] = 0;
|
|
strcpy(replace, "L");
|
|
break;
|
|
default:
|
|
is_set = false;
|
|
}
|
|
}
|
|
|
|
/* case; beginning with r R l L, with separator after it */
|
|
if (!is_set && is_char_sep(r_name[1])) {
|
|
is_set = true;
|
|
switch (r_name[0]) {
|
|
case 'l':
|
|
strcpy(replace, "r");
|
|
BLI_strncpy(suffix, r_name + 1, name_len);
|
|
prefix[0] = 0;
|
|
break;
|
|
case 'r':
|
|
strcpy(replace, "l");
|
|
BLI_strncpy(suffix, r_name + 1, name_len);
|
|
prefix[0] = 0;
|
|
break;
|
|
case 'L':
|
|
strcpy(replace, "R");
|
|
BLI_strncpy(suffix, r_name + 1, name_len);
|
|
prefix[0] = 0;
|
|
break;
|
|
case 'R':
|
|
strcpy(replace, "L");
|
|
BLI_strncpy(suffix, r_name + 1, name_len);
|
|
prefix[0] = 0;
|
|
break;
|
|
default:
|
|
is_set = false;
|
|
}
|
|
}
|
|
|
|
if (!is_set && len > 5) {
|
|
/* hrms, why test for a separator? lets do the rule 'ultimate left or right' */
|
|
if (((index = BLI_strcasestr(prefix, "right")) == prefix) || (index == prefix + len - 5)) {
|
|
is_set = true;
|
|
if (index[0] == 'r') {
|
|
strcpy(replace, "left");
|
|
}
|
|
else {
|
|
strcpy(replace, (index[1] == 'I') ? "LEFT" : "Left");
|
|
}
|
|
*index = 0;
|
|
BLI_strncpy(suffix, index + 5, name_len);
|
|
}
|
|
else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || (index == prefix + len - 4)) {
|
|
is_set = true;
|
|
if (index[0] == 'l') {
|
|
strcpy(replace, "right");
|
|
}
|
|
else {
|
|
strcpy(replace, (index[1] == 'E') ? "RIGHT" : "Right");
|
|
}
|
|
*index = 0;
|
|
BLI_strncpy(suffix, index + 4, name_len);
|
|
}
|
|
}
|
|
|
|
return BLI_snprintf_rlen(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number);
|
|
}
|
|
|
|
/* Unique name utils. */
|
|
|
|
bool BLI_uniquename_cb(UniquenameCheckCallback unique_check,
|
|
void *arg,
|
|
const char *defname,
|
|
char delim,
|
|
char *name,
|
|
size_t name_len)
|
|
{
|
|
if (name[0] == '\0') {
|
|
BLI_strncpy(name, defname, name_len);
|
|
}
|
|
|
|
if (unique_check(arg, name)) {
|
|
char numstr[16];
|
|
char *tempname = alloca(name_len);
|
|
char *left = alloca(name_len);
|
|
int number;
|
|
size_t len = BLI_split_name_num(left, &number, name, delim);
|
|
do {
|
|
/* add 1 to account for \0 */
|
|
const size_t numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number) + 1;
|
|
|
|
/* highly unlikely the string only has enough room for the number
|
|
* but support anyway */
|
|
if ((len == 0) || (numlen >= name_len)) {
|
|
/* number is know not to be utf-8 */
|
|
BLI_strncpy(tempname, numstr, name_len);
|
|
}
|
|
else {
|
|
char *tempname_buf;
|
|
tempname_buf = tempname + BLI_strncpy_utf8_rlen(tempname, left, name_len - numlen);
|
|
memcpy(tempname_buf, numstr, numlen);
|
|
}
|
|
} while (unique_check(arg, tempname));
|
|
|
|
BLI_strncpy(name, tempname, name_len);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Generic function to set a unique name. It is only designed to be used in situations
|
|
* where the name is part of the struct.
|
|
*
|
|
* For places where this is used, see constraint.c for example...
|
|
*
|
|
* \param name_offset: should be calculated using `offsetof(structname, membername)`
|
|
* macro from `stddef.h`
|
|
*/
|
|
static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offset)
|
|
{
|
|
Link *link;
|
|
|
|
for (link = list->first; link; link = link->next) {
|
|
if (link != vlink) {
|
|
if (STREQ(POINTER_OFFSET((const char *)link, name_offset), name)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool uniquename_unique_check(void *arg, const char *name)
|
|
{
|
|
struct {
|
|
ListBase *lb;
|
|
void *vlink;
|
|
int name_offset;
|
|
} *data = arg;
|
|
return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offset);
|
|
}
|
|
|
|
bool BLI_uniquename(
|
|
ListBase *list, void *vlink, const char *defname, char delim, int name_offset, size_t name_len)
|
|
{
|
|
struct {
|
|
ListBase *lb;
|
|
void *vlink;
|
|
int name_offset;
|
|
} data;
|
|
data.lb = list;
|
|
data.vlink = vlink;
|
|
data.name_offset = name_offset;
|
|
|
|
BLI_assert(name_len > 1);
|
|
|
|
/* See if we are given an empty string */
|
|
if (ELEM(NULL, vlink, defname)) {
|
|
return false;
|
|
}
|
|
|
|
return BLI_uniquename_cb(uniquename_unique_check,
|
|
&data,
|
|
defname,
|
|
delim,
|
|
POINTER_OFFSET(vlink, name_offset),
|
|
name_len);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/** \name Join Strings
|
|
*
|
|
* For non array versions of these functions, use the macros:
|
|
* - #BLI_string_join
|
|
* - #BLI_string_joinN
|
|
* - #BLI_string_join_by_sep_charN
|
|
* - #BLI_string_join_by_sep_char_with_tableN
|
|
*
|
|
* \{ */
|
|
|
|
size_t BLI_string_join_array(char *result,
|
|
size_t result_len,
|
|
const char *strings[],
|
|
uint strings_len)
|
|
{
|
|
char *c = result;
|
|
char *c_end = &result[result_len - 1];
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
const char *p = strings[i];
|
|
while (*p && (c < c_end)) {
|
|
*c++ = *p++;
|
|
}
|
|
}
|
|
*c = '\0';
|
|
return (size_t)(c - result);
|
|
}
|
|
|
|
size_t BLI_string_join_array_by_sep_char(
|
|
char *result, size_t result_len, char sep, const char *strings[], uint strings_len)
|
|
{
|
|
char *c = result;
|
|
char *c_end = &result[result_len - 1];
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
if (i != 0) {
|
|
if (c < c_end) {
|
|
*c++ = sep;
|
|
}
|
|
}
|
|
const char *p = strings[i];
|
|
while (*p && (c < c_end)) {
|
|
*c++ = *p++;
|
|
}
|
|
}
|
|
*c = '\0';
|
|
return (size_t)(c - result);
|
|
}
|
|
|
|
char *BLI_string_join_arrayN(const char *strings[], uint strings_len)
|
|
{
|
|
uint total_len = 1;
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
total_len += strlen(strings[i]);
|
|
}
|
|
char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
|
|
char *c = result;
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
c += BLI_strcpy_rlen(c, strings[i]);
|
|
}
|
|
/* Only needed when `strings_len == 0`. */
|
|
*c = '\0';
|
|
return result;
|
|
}
|
|
|
|
char *BLI_string_join_array_by_sep_charN(char sep, const char *strings[], uint strings_len)
|
|
{
|
|
uint total_len = 0;
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
total_len += strlen(strings[i]) + 1;
|
|
}
|
|
if (total_len == 0) {
|
|
total_len = 1;
|
|
}
|
|
|
|
char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
|
|
char *c = result;
|
|
if (strings_len != 0) {
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
c += BLI_strcpy_rlen(c, strings[i]);
|
|
*c = sep;
|
|
c++;
|
|
}
|
|
c--;
|
|
}
|
|
*c = '\0';
|
|
return result;
|
|
}
|
|
|
|
char *BLI_string_join_array_by_sep_char_with_tableN(char sep,
|
|
char *table[],
|
|
const char *strings[],
|
|
uint strings_len)
|
|
{
|
|
uint total_len = 0;
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
total_len += strlen(strings[i]) + 1;
|
|
}
|
|
if (total_len == 0) {
|
|
total_len = 1;
|
|
}
|
|
|
|
char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
|
|
char *c = result;
|
|
if (strings_len != 0) {
|
|
for (uint i = 0; i < strings_len; i++) {
|
|
table[i] = c; /* <-- only difference to BLI_string_join_array_by_sep_charN. */
|
|
c += BLI_strcpy_rlen(c, strings[i]);
|
|
*c = sep;
|
|
c++;
|
|
}
|
|
c--;
|
|
}
|
|
*c = '\0';
|
|
return result;
|
|
}
|
|
|
|
/** \} */
|