__strtok is now reentrant
[libstring.git] / src / libstring.c
blobcf538d73277c704352082ad0e1330465ff80a8b6
1 /*
2 * libstring
3 * https://github.com/carmesim/libstring
5 * Copyright (c) 2020 Vinícius R. Miguel, Ivan dos Santos Muniz
6 * <vinicius.miguel at unifesp.br>
7 * <ivan.muniz at unifesp.br>
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in all
17 * copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
28 #include "libstring.h"
29 #include <stdio.h>
31 //! **** Defining internal functions **** !//
33 //!
34 //! \brief __malloc Simple error-printing wrapper around malloc.
35 //! \param size Quantity of memory to be allocated
36 //! \return A pointer to the allocated memory.
37 //!
38 void * __malloc(size_t size)
40 void * ptr = malloc(size);
41 if(!ptr)
43 fprintf(stderr, "Fatal error: malloc failed.\n");
44 // TODO: exit?
46 return ptr;
49 //!
50 //! \brief __strlen Portable and simple reimplementation of strlen
51 //! \param s The NUL-terminated char array whose length will be calculated.
52 //! \return The size of the given char array.
53 //!
54 size_t __strlen(const char *s)
56 size_t i;
57 for (i = 0; s[i] != '\0'; i++);
58 return i;
61 //!
62 //! \brief __memeq Compares two char arrays and returns true if they're equal on their first `size` elements.
63 //! \param str1 First char array
64 //! \param str2 Second char array
65 //! \param size Number of elements to be compared
66 //! \return Returns true if both char arrays are equal on their first `size` elements.
67 //! In short, __memeq(s1, s2, n) <-> !memcmp(s1, s2, n)
68 bool __memeq(char * str1, char * str2, size_t size)
70 if (size)
72 char *pstr1 = str1, *pstr2 = str2;
74 for(; size; --size)
76 if (*pstr1++ != *pstr2++)
78 return false;
82 return true;
85 //!
86 //! \brief __strstr Verifies if `find` is a substring within `str`.
87 //! \param str A char array.
88 //! \param find The possible substring within `str`.
89 //! \return Returns NULL if `find` is not a substring of `str`.
90 //! This function does *not* verify if __strlen(find) > __strlen(str).
91 //!
92 char * __strstr(char * str, char *find)
94 char c = *find++;
95 if (c != '\0')
97 char sc;
98 size_t len =__strlen(find);
101 for(sc = *str++; sc != c; sc = *str++)
103 if (sc == '\0')
105 return NULL;
108 } while(!__memeq(str, find, len));
109 str--;
111 return str;
115 //! \brief __strtok
116 //! \param str
117 //! \param delim
118 //! \return
119 //! Based on a version of strtok by Chris Dodd.
120 //! Update: this should be reentrant/safer now.
121 //! Note: this *will* modify `str`. Duplicate the string before usage.
122 char * __strtok(char *str, char *delim, char ** prev)
124 if (!str)
125 str = *prev;
126 if (str)
128 char *end = __strstr(str, delim);
129 if (end)
131 *prev = end + __strlen(delim);
132 *end = 0;
133 } else
135 *prev = 0;
138 return str;
142 //! \brief __memcpy Simple implementation of memcpy. This implementation is specific to char arrays.
143 //! \param dest The destination char array, whose contents will be written to src.
144 //! \param src The source char array, where the contents of dest will be written to.
145 //! \param n The quantity of elements to be copied.
146 //! \return Returns the destination char array.
148 char * __memcpy(char * dest, const char *src, size_t n) {
149 while (n--)
151 *dest++ = *src++;
153 return dest;
157 //! \brief __strcpy A simple string copy function. It will always null-terminate the destination char array.
158 //! \param dest Destination char array.
159 //! \param src Source char array.
160 //! \param size Quantitity of elements to be copied. It may be larger than __strlen(src), but the function will not write more than __strlen(src).
162 size_t __strcpy(char *dest, const char *src, size_t size)
164 if(!size)
166 fprintf(stderr, "In __strcpy: `size` should not be 0.\n");
167 return 0;
170 size_t source_len = __strlen(src);
171 size_t n;
173 if (size < source_len) // Truncate to the size of the smaller string
175 n = size;
176 } else
178 n = source_len;
181 __memcpy(dest, src, n); // Copy the contents of src to dest
182 dest[n] = '\0'; // Null-terminate dest.
183 return source_len;
186 size_t __strcat(char *dest, const char *src, size_t size)
188 size_t dest_len = __strlen(dest);
189 if (dest_len < size)
191 return(dest_len + __strcpy(dest + dest_len, src, size - dest_len));
193 return(dest_len + __strlen(src));
197 * \struct alloc_node A single node of the allocation linked list.
198 * \param nbytes The number of bytes to be allocated.
199 * \property val A pointer to struct cstr.
200 * \property next A pointer to the next node in the allocation list.
202 struct alloc_node
204 cstr_t * val;
205 struct alloc_node * next;
208 //! We keep a simple linked list of heap allocations as to allow for string_free_all()
209 struct alloc_node * alloc_list_head = NULL;
212 //! \brief string_alloc Allocates memory for a cstr_t * and adds it to the allocation list.
213 //! \param nbytes The number of bytes to be allocated.
214 //! \return Returns a new cstr_t* allocated within the alloc. list.
216 cstr_t * string_alloc (size_t nbytes)
218 if (!alloc_list_head) // If the list's head hasn't been initialized
220 alloc_list_head = __malloc(sizeof(struct alloc_node));
221 alloc_list_head->val = __malloc(sizeof(struct cstr));
222 alloc_list_head->val->value = __malloc(nbytes + 1);
223 alloc_list_head->val->size = nbytes-1; //! Remove one from nbytes because it includes the NULL-terminator.
224 alloc_list_head->val->reserved = nbytes;
225 alloc_list_head->next = NULL;
226 return alloc_list_head->val;
229 struct alloc_node * current = alloc_list_head;
230 while (current->next)
231 current = current->next;
233 current->next = __malloc(sizeof(struct alloc_node));
234 current->next->val = __malloc(sizeof(struct cstr));
235 current->next->val->value = __malloc(nbytes + 1);
236 current->next->val->size = nbytes-1;
237 current->next->val->reserved = nbytes;
238 current->next->next = NULL;
239 return current->next->val;
243 //! \brief string_free_all Frees all heap memory allocated by libstring.
245 void string_free_all (void)
247 struct alloc_node * current = alloc_list_head;
248 while (current)
250 struct alloc_node * temp = current;
251 current = current->next;
252 free(temp->val->value);
253 free(temp->val);
254 free(temp);
259 //! \brief string_to_lower_case Alters a string to contain only lower-case characters.
260 //! \param origin The string whose value will be converted to lower-case characters. This parameters does not get modified.
261 //! \return A brand new cstr_t * that contains the altered value.
263 cstr_t * string_to_lower_case(cstr_t * origin)
265 cstr_t* lower;
266 lower = string_init(origin->value);
267 __strcpy(lower->value, origin->value, origin->size);
269 size_t i;
270 for(i=0; i<lower->size; i++)
272 if ((lower->value[i] >= 'A') && (lower->value[i] <= 'Z'))
274 lower->value[i] |= ' ';
277 return lower;
281 //! \brief string_to_upper_case Alters a string to contain only upper-case characters.
282 //! \param origin The string whose value will be converted to upper-case characters. This parameters does not get modified.
283 //! \return A brand new cstr_t * that contains the altered value.
284 //! Makes use of bit manipulation to alter the ASCII values.
286 cstr_t * string_to_upper_case(cstr_t * origin)
288 cstr_t* upper;
289 upper = string_init(origin->value);
290 __strcpy(upper->value, origin->value, origin->size);
292 size_t i;
293 for(i=0; i<upper->size; i++)
295 if ((upper->value[i] >= 'a') && (upper->value[i] <= 'z'))
297 upper->value[i] &= '_';
300 return upper;
304 //! \brief string_init Initializes a new cstr_t *.
305 //! \param origin The char array to be the value of the new string.
306 //! \return A brand new cstr_t *.
308 cstr_t * string_init(const char * origin)
310 cstr_t * new = string_alloc(__strlen(origin)+1);
311 __strcpy(new->value, origin, __strlen(origin));
312 return new;
316 //! \brief string_update Updates the value of an cstr_t *. Increases its memory reservation if needed.
317 //! \param str The cstr_t * to be modified.
318 //! \param new_val The new char array value.
319 //! \return The new string's size.
321 size_t string_update(cstr_t * str, const char * new_val)
323 size_t new_string_len = __strlen(new_val);
324 if (new_string_len > str->reserved)
326 if (!string_reserve(str, new_string_len+1))
328 fprintf(stderr, "In string_replace: string_reserve(str, %zu) failed.", new_string_len+1);
329 return 0;
333 __strcpy(str->value, new_val, new_string_len);
334 //printf("strlen: %zu, new_string_len: %zu\n", __strlen(str->value), new_string_len);
335 str->size = new_string_len;
336 return new_string_len;
340 //! \brief string_contains
341 //! \param str1
342 //! \param str2
343 //! \return
345 bool string_contains(cstr_t * str1, const char * str2)
347 if (!str1)
349 fprintf(stderr, "In string_contains: `str1` unitialized.\n");
350 return false;
353 if (str1->size < __strlen(str2))
355 //! str2 is bigger than str1, so it can't be a substring.
356 return false;
359 if(__strstr(str1->value, (char *) str2))
361 return true; // If __strstr returned a non-NULL pointer, str2 is a substring of str1.
363 return false;
367 * \brief Initializes a new string, a poitner to cstr_t
368 * \param str The cstr_t * whose capacity will be altered.
369 * \param capacity The new memory reserve of the cstr_t *.
370 * \retval bool Returns true if the operation was succesful, false otherwise.
372 bool string_reserve(cstr_t *str, size_t capacity)
374 if (!str)
376 fprintf(stderr, "In string_reserve: string not initialized.\n");
377 return false;
380 if (capacity < str->size)
382 //! TODO: Implement truncation?
383 fprintf(stderr, "In string_reserve: new capacity supplied is smaller than the string's current size.\n");
384 return false;
387 char * val_backup = __malloc(str->size + 1);
388 str->value = realloc(str->value, capacity);
389 if(!str->value)
391 //! TODO: needs testing
392 //! I couldn't get realloc to fail in order to test
393 fprintf(stderr, "In string_reserve: reallocation failed with capacity %zu\n", capacity);
394 str->value = val_backup;
395 return false;
398 //! In this case, reallocation worked
399 free (val_backup); //! Get rid of the backup
400 str->reserved = capacity;
401 return true;
405 //! \brief string_concat_to Concatenates a string str2 to str1.
406 //! \param str1
407 //! \param str2
408 //! \return
410 size_t string_concat_to(cstr_t * str1, const char * str2)
412 if (!str1)
414 fprintf(stderr, "In string_concat_to: str1 is unitialized.\n");
415 return 0;
417 size_t str2len = __strlen(str2);
419 if(!str2len)
420 return 0;
422 if(str1->reserved < str1->size + str2len + 1)
424 if(!string_reserve(str1, str1->size + str2len + 1))
426 fprintf(stderr, "In string_concat_to: string_reserve failed.\n");
427 return 0;
431 str1->size += str2len;
432 __strcat(str1->value, str2, str1->size);
433 return str2len;
437 //! \brief string_concat Concatenates str2 to str1 and returns that value into a new cstr_t *.
438 //! \param str1
439 //! \param str2
440 //! \return A new value containing str1 + str2
442 cstr_t * string_concat(cstr_t * str1, const char * str2)
444 cstr_t * new = string_init(str1->value);
446 size_t str2len = __strlen(str2);
447 if(new->reserved < str1->size + str2len + 1)
449 if(!string_reserve(new, new->size + str2len + 1))
451 fprintf(stderr, "In string_concat: string_reserve failed.\n");
452 return new;
455 new->size = str1->size + str2len;
456 __strcat(new->value, str2, new->size);
457 return new;
461 //! \brief string_replace_char Replaces all instances of `before` in str to `after`.
462 //! \param str The cstr_t * to be altered.
463 //! \param before The char to be replaced by `after`.
464 //! \param after The char to replace `before`.
465 //! \return The quantity of changed characters.
467 //! TODO: should this function return a copy and maintain the original untouched?
469 size_t string_replace_char(cstr_t *str, char before, char after)
471 if(!str)
473 fprintf(stderr, "In string_replace_char: str is uninitialized.\n");
474 return 0;
477 size_t i, modified = 0;
479 for(i=0; i < str->size; i++)
481 if(str->value[i] == before)
483 str->value[i] = after;
484 modified++;
487 return modified;
492 //! \brief string_swap Swaps the content of str1 with str2.
493 //! \param str1 An initialized cstr_t *.
494 //! \param str2 An initialized cstr_t *.
495 //! \return A success-run boolean.
497 bool string_swap(cstr_t * str1, cstr_t * str2)
499 if (!str1)
501 fprintf(stderr, "In string_swap: str1 uninitialized.\n");
502 return false;
504 if (!str2)
506 fprintf(stderr, "In string_swap: str2 unitialized.\n");
507 return false;
510 size_t str1_val_size = str1->size;
511 char * str1_val_backup = malloc(str1_val_size + 1);
512 if(!str1_val_backup)
514 fprintf(stderr, "In string_swap: malloc failed.\n");
515 return false;
518 __strcpy(str1_val_backup, str1->value, str1_val_size);
519 size_t str1_res_backup = str1->reserved;
520 free(str1->value);
521 str1->value = str2->value;
522 str1->size = str2->size;
523 str1->reserved = str2->reserved;
525 str2->value = str1_val_backup;
526 str2->size = str1_val_size;
527 str2->reserved = str1_res_backup;
529 return true;