Blender  V3.3
string_utils.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2017 Blender Foundation. All rights reserved. */
3 
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "MEM_guardedalloc.h"
13 
14 #include "BLI_string.h"
15 #include "BLI_string_utf8.h"
16 #include "BLI_string_utils.h"
17 #include "BLI_utildefines.h"
18 
19 #include "DNA_listBase.h"
20 
21 #ifdef __GNUC__
22 # pragma GCC diagnostic error "-Wsign-conversion"
23 #endif
24 
25 size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim)
26 {
27  const size_t name_len = strlen(name);
28 
29  *nr = 0;
30  memcpy(left, name, (name_len + 1) * sizeof(char));
31 
32  /* name doesn't end with a delimiter "foo." */
33  if ((name_len > 1 && name[name_len - 1] == delim) == 0) {
34  size_t a = name_len;
35  while (a--) {
36  if (name[a] == delim) {
37  left[a] = '\0'; /* truncate left part here */
38  *nr = atol(name + a + 1);
39  /* casting down to an int, can overflow for large numbers */
40  if (*nr < 0) {
41  *nr = 0;
42  }
43  return a;
44  }
45  if (isdigit(name[a]) == 0) {
46  /* non-numeric suffix - give up */
47  break;
48  }
49  }
50  }
51 
52  return name_len;
53 }
54 
55 bool BLI_string_is_decimal(const char *string)
56 {
57  if (*string == '\0') {
58  return false;
59  }
60 
61  /* Keep iterating over the string until a non-digit is found. */
62  while (isdigit(*string)) {
63  string++;
64  }
65 
66  /* If the non-digit we found is the terminating \0, everything was digits. */
67  return *string == '\0';
68 }
69 
70 static bool is_char_sep(const char c)
71 {
72  return ELEM(c, '.', ' ', '-', '_');
73 }
74 
75 void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len)
76 {
77  size_t len = BLI_strnlen(string, str_len);
78  size_t i;
79 
80  r_body[0] = r_suf[0] = '\0';
81 
82  for (i = len; i > 0; i--) {
83  if (is_char_sep(string[i])) {
84  BLI_strncpy(r_body, string, i + 1);
85  BLI_strncpy(r_suf, string + i, (len + 1) - i);
86  return;
87  }
88  }
89 
90  memcpy(r_body, string, len + 1);
91 }
92 
93 void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len)
94 {
95  size_t len = BLI_strnlen(string, str_len);
96  size_t i;
97 
98  r_body[0] = r_pre[0] = '\0';
99 
100  for (i = 1; i < len; i++) {
101  if (is_char_sep(string[i])) {
102  i++;
103  BLI_strncpy(r_pre, string, i + 1);
104  BLI_strncpy(r_body, string + i, (len + 1) - i);
105  return;
106  }
107  }
108 
109  BLI_strncpy(r_body, string, len);
110 }
111 
112 size_t BLI_string_flip_side_name(char *r_name,
113  const char *from_name,
114  const bool strip_number,
115  const size_t name_len)
116 {
117  size_t len;
118  char *prefix = alloca(name_len); /* The part before the facing */
119  char *suffix = alloca(name_len); /* The part after the facing */
120  char *replace = alloca(name_len); /* The replacement string */
121  char *number = alloca(name_len); /* The number extension string */
122  char *index = NULL;
123  bool is_set = false;
124 
125  *prefix = *suffix = *replace = *number = '\0';
126 
127  /* always copy the name, since this can be called with an uninitialized string */
128  len = BLI_strncpy_rlen(r_name, from_name, name_len);
129  if (len < 3) {
130  /* we don't do names like .R or .L */
131  return len;
132  }
133 
134  /* We first check the case with a .### extension, let's find the last period */
135  if (isdigit(r_name[len - 1])) {
136  index = strrchr(r_name, '.'); /* Last occurrence. */
137  if (index && isdigit(index[1])) { /* Doesn't handle case `bone.1abc2` correct..., whatever! */
138  if (strip_number == false) {
139  BLI_strncpy(number, index, name_len);
140  }
141  *index = 0;
142  len = BLI_strnlen(r_name, name_len);
143  }
144  }
145 
146  BLI_strncpy(prefix, r_name, name_len);
147 
148  /* First case; separator (`.` or `_`) with extensions in `r R l L`. */
149  if ((len > 1) && is_char_sep(r_name[len - 2])) {
150  is_set = true;
151  switch (r_name[len - 1]) {
152  case 'l':
153  prefix[len - 1] = 0;
154  strcpy(replace, "r");
155  break;
156  case 'r':
157  prefix[len - 1] = 0;
158  strcpy(replace, "l");
159  break;
160  case 'L':
161  prefix[len - 1] = 0;
162  strcpy(replace, "R");
163  break;
164  case 'R':
165  prefix[len - 1] = 0;
166  strcpy(replace, "L");
167  break;
168  default:
169  is_set = false;
170  }
171  }
172 
173  /* case; beginning with r R l L, with separator after it */
174  if (!is_set && is_char_sep(r_name[1])) {
175  is_set = true;
176  switch (r_name[0]) {
177  case 'l':
178  strcpy(replace, "r");
179  BLI_strncpy(suffix, r_name + 1, name_len);
180  prefix[0] = 0;
181  break;
182  case 'r':
183  strcpy(replace, "l");
184  BLI_strncpy(suffix, r_name + 1, name_len);
185  prefix[0] = 0;
186  break;
187  case 'L':
188  strcpy(replace, "R");
189  BLI_strncpy(suffix, r_name + 1, name_len);
190  prefix[0] = 0;
191  break;
192  case 'R':
193  strcpy(replace, "L");
194  BLI_strncpy(suffix, r_name + 1, name_len);
195  prefix[0] = 0;
196  break;
197  default:
198  is_set = false;
199  }
200  }
201 
202  if (!is_set && len > 5) {
203  /* hrms, why test for a separator? lets do the rule 'ultimate left or right' */
204  if (((index = BLI_strcasestr(prefix, "right")) == prefix) || (index == prefix + len - 5)) {
205  is_set = true;
206  if (index[0] == 'r') {
207  strcpy(replace, "left");
208  }
209  else {
210  strcpy(replace, (index[1] == 'I') ? "LEFT" : "Left");
211  }
212  *index = 0;
213  BLI_strncpy(suffix, index + 5, name_len);
214  }
215  else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || (index == prefix + len - 4)) {
216  is_set = true;
217  if (index[0] == 'l') {
218  strcpy(replace, "right");
219  }
220  else {
221  strcpy(replace, (index[1] == 'E') ? "RIGHT" : "Right");
222  }
223  *index = 0;
224  BLI_strncpy(suffix, index + 4, name_len);
225  }
226  }
227 
228  return BLI_snprintf_rlen(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number);
229 }
230 
231 /* Unique name utils. */
232 
234  void *arg,
235  const char *defname,
236  char delim,
237  char *name,
238  size_t name_len)
239 {
240  if (name[0] == '\0') {
241  BLI_strncpy(name, defname, name_len);
242  }
243 
244  if (unique_check(arg, name)) {
245  char numstr[16];
246  char *tempname = alloca(name_len);
247  char *left = alloca(name_len);
248  int number;
249  size_t len = BLI_split_name_num(left, &number, name, delim);
250  do {
251  /* add 1 to account for \0 */
252  const size_t numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number) + 1;
253 
254  /* highly unlikely the string only has enough room for the number
255  * but support anyway */
256  if ((len == 0) || (numlen >= name_len)) {
257  /* number is know not to be utf-8 */
258  BLI_strncpy(tempname, numstr, name_len);
259  }
260  else {
261  char *tempname_buf;
262  tempname_buf = tempname + BLI_strncpy_utf8_rlen(tempname, left, name_len - numlen);
263  memcpy(tempname_buf, numstr, numlen);
264  }
265  } while (unique_check(arg, tempname));
266 
267  BLI_strncpy(name, tempname, name_len);
268 
269  return true;
270  }
271 
272  return false;
273 }
274 
284 static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offset)
285 {
286  Link *link;
287 
288  for (link = list->first; link; link = link->next) {
289  if (link != vlink) {
290  if (STREQ(POINTER_OFFSET((const char *)link, name_offset), name)) {
291  return true;
292  }
293  }
294  }
295 
296  return false;
297 }
298 
299 static bool uniquename_unique_check(void *arg, const char *name)
300 {
301  struct {
302  ListBase *lb;
303  void *vlink;
304  int name_offset;
305  } *data = arg;
306  return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offset);
307 }
308 
310  ListBase *list, void *vlink, const char *defname, char delim, int name_offset, size_t name_len)
311 {
312  struct {
313  ListBase *lb;
314  void *vlink;
315  int name_offset;
316  } data;
317  data.lb = list;
318  data.vlink = vlink;
319  data.name_offset = name_offset;
320 
321  BLI_assert(name_len > 1);
322 
323  /* See if we are given an empty string */
324  if (ELEM(NULL, vlink, defname)) {
325  return false;
326  }
327 
329  &data,
330  defname,
331  delim,
332  POINTER_OFFSET(vlink, name_offset),
333  name_len);
334 }
335 
336 /* ------------------------------------------------------------------------- */
348  size_t result_len,
349  const char *strings[],
350  uint strings_len)
351 {
352  char *c = result;
353  char *c_end = &result[result_len - 1];
354  for (uint i = 0; i < strings_len; i++) {
355  const char *p = strings[i];
356  while (*p && (c < c_end)) {
357  *c++ = *p++;
358  }
359  }
360  *c = '\0';
361  return c;
362 }
363 
365  char *result, size_t result_len, char sep, const char *strings[], uint strings_len)
366 {
367  char *c = result;
368  char *c_end = &result[result_len - 1];
369  for (uint i = 0; i < strings_len; i++) {
370  if (i != 0) {
371  if (c < c_end) {
372  *c++ = sep;
373  }
374  }
375  const char *p = strings[i];
376  while (*p && (c < c_end)) {
377  *c++ = *p++;
378  }
379  }
380  *c = '\0';
381  return c;
382 }
383 
384 char *BLI_string_join_arrayN(const char *strings[], uint strings_len)
385 {
386  uint total_len = 1;
387  for (uint i = 0; i < strings_len; i++) {
388  total_len += strlen(strings[i]);
389  }
390  char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
391  char *c = result;
392  for (uint i = 0; i < strings_len; i++) {
393  c += BLI_strcpy_rlen(c, strings[i]);
394  }
395  /* Only needed when `strings_len == 0`. */
396  *c = '\0';
397  return result;
398 }
399 
400 char *BLI_string_join_array_by_sep_charN(char sep, const char *strings[], uint strings_len)
401 {
402  uint total_len = 0;
403  for (uint i = 0; i < strings_len; i++) {
404  total_len += strlen(strings[i]) + 1;
405  }
406  if (total_len == 0) {
407  total_len = 1;
408  }
409 
410  char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
411  char *c = result;
412  if (strings_len != 0) {
413  for (uint i = 0; i < strings_len; i++) {
414  c += BLI_strcpy_rlen(c, strings[i]);
415  *c = sep;
416  c++;
417  }
418  c--;
419  }
420  *c = '\0';
421  return result;
422 }
423 
425  char *table[],
426  const char *strings[],
427  uint strings_len)
428 {
429  uint total_len = 0;
430  for (uint i = 0; i < strings_len; i++) {
431  total_len += strlen(strings[i]) + 1;
432  }
433  if (total_len == 0) {
434  total_len = 1;
435  }
436 
437  char *result = MEM_mallocN(sizeof(char) * total_len, __func__);
438  char *c = result;
439  if (strings_len != 0) {
440  for (uint i = 0; i < strings_len; i++) {
441  table[i] = c; /* <-- only difference to BLI_string_join_array_by_sep_charN. */
442  c += BLI_strcpy_rlen(c, strings[i]);
443  *c = sep;
444  c++;
445  }
446  c--;
447  }
448  *c = '\0';
449  return result;
450 }
451 
#define BLI_assert(a)
Definition: BLI_assert.h:46
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:120
char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:538
size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:899
size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:134
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
bool(* UniquenameCheckCallback)(void *arg, const char *name)
unsigned int uint
Definition: BLI_sys_types.h:67
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define STREQ(a, b)
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
int len
Definition: draw_manager.c:108
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static int left
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
static bool is_char_sep(const char c)
Definition: string_utils.c:70
size_t BLI_string_flip_side_name(char *r_name, const char *from_name, const bool strip_number, const size_t name_len)
Definition: string_utils.c:112
void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len)
Definition: string_utils.c:93
bool BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim, int name_offset, size_t name_len)
Definition: string_utils.c:309
char * BLI_string_join_arrayN(const char *strings[], uint strings_len)
Definition: string_utils.c:384
char * BLI_string_join_array_by_sep_charN(char sep, const char *strings[], uint strings_len)
Definition: string_utils.c:400
size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim)
Definition: string_utils.c:25
char * BLI_string_join_array(char *result, size_t result_len, const char *strings[], uint strings_len)
Definition: string_utils.c:347
bool BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_len)
Definition: string_utils.c:233
void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len)
Definition: string_utils.c:75
static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offset)
Definition: string_utils.c:284
char * BLI_string_join_array_by_sep_char_with_tableN(char sep, char *table[], const char *strings[], uint strings_len)
Definition: string_utils.c:424
char * BLI_string_join_array_by_sep_char(char *result, size_t result_len, char sep, const char *strings[], uint strings_len)
Definition: string_utils.c:364
bool BLI_string_is_decimal(const char *string)
Definition: string_utils.c:55
static bool uniquename_unique_check(void *arg, const char *name)
Definition: string_utils.c:299
void * first
Definition: DNA_listBase.h:31