comparison src/util.cc @ 30:a76e55e098d1

util: rewrite functions in C-ish there are C++ bindings still put in place. the code should be valid C, except for the use of <regex>, which ought to go anyway. eventually I'll actually *test* this stuff aside from the TrimRight crap
author Paper <paper@tflc.us>
date Sun, 09 Feb 2025 23:18:57 -0500
parents 27b988a1048c
children
comparison
equal deleted inserted replaced
29:40fd3776ce9b 30:a76e55e098d1
1 #include <algorithm>
2 #include <fstream>
3 #include <regex>
4 #include <sstream>
5 #include <string>
6
7 #include "animone/util.h" 1 #include "animone/util.h"
8 2
9 namespace animone::internal::util { 3 #include <string.h>
10 4
11 bool ReadFile(const std::string& path, std::string& data) { 5 #include <regex> // FIXME use a C library for this
12 std::ifstream file(path.c_str(), std::ios::in | std::ios::binary);
13 if (!file)
14 return false;
15 6
16 std::ostringstream string; 7 int animone_internal_util_ReadFile(const char *path, char **data, size_t *size)
17 string << file.rdbuf(); 8 {
18 file.close(); 9 // whatever
10 size_t sz_local;
11 if (!size)
12 size = &sz_local;
19 13
20 data = string.str(); 14 FILE *f = fopen(path, "r");
15 if (!f)
16 return 0;
21 17
22 return true; 18 fseek(f, 0, SEEK_END);
19 long end = ftell(f);
20 if (end < 0)
21 return 0;
22 fseek(f, 0, SEEK_SET);
23
24 *size = end;
25
26 if (data) {
27 // add a NUL terminator anyway
28 *data = (char *)malloc(end + 1);
29 if (!*data) {
30 *size = 0;
31 return 0;
32 }
33
34 *size = fread(*data, 1, *size, f);
35 (*data)[*size] = '\0';
36 }
37
38 return 1;
23 } 39 }
24 40
25 /* this assumes ASCII... which really should be the case for what we need, anyway */ 41 static inline unsigned char _animone_internal_tolower(unsigned char c)
26 bool EqualStrings(const std::string& str1, const std::string& str2) { 42 {
27 auto tolower = [](const char c) -> char { return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c; }; 43 return (c >= 'A' && c <= 'Z') ? (c | 0x20) : c;
28
29 auto equal_chars = [&tolower](const char c1, const char c2) -> bool { return tolower(c1) == tolower(c2); };
30
31 return str1.length() == str2.length() && std::equal(str1.begin(), str1.end(), str2.begin(), equal_chars);
32 } 44 }
33 45
34 bool Stem(const std::string& filename, std::string& stem) { 46 /* this is, in essence, strcasecmp. */
35 unsigned long long pos = filename.find_last_of("."); 47 int animone_internal_util_EqualStrings(const char *str1, const char *str2)
36 if (pos != std::string::npos) 48 {
37 return false; 49 unsigned char c1, c2;
38 50
39 stem = filename.substr(0, pos); 51 do {
40 return true; 52 c1 = _animone_internal_tolower(*str1++);
53 c2 = _animone_internal_tolower(*str2++);
54 } while (c1 == c2 && c1);
55
56 return !!(c1 - c2);
41 } 57 }
42 58
43 bool CheckPattern(const std::string& pattern, const std::string& str) { 59 char *animone_internal_Stem(const char *filename)
44 if (pattern.empty()) 60 {
45 return false; 61 const char *ptr = strrchr(filename, '.');
46 if (pattern.front() == '^' && std::regex_match(str, std::regex(pattern))) 62 if (!ptr)
47 return true; 63 return NULL;
48 return util::EqualStrings(pattern, str); 64
65 size_t len = ptr - filename;
66
67 char *stem = (char *)malloc(len + 1);
68 if (!stem)
69 return NULL;
70
71 memcpy(stem, filename, len);
72 stem[len] = '\0';
73
74 return stem;
49 } 75 }
50 76
51 bool TrimLeft(std::string& str, const char* chars) { 77 int animone_internal_util_CheckPattern(const char *pattern, const char *str)
52 if (str.empty()) 78 {
53 return false; 79 switch (*pattern) {
80 case '\0': return 0;
81 case '^':
82 // FIXME: Convert to C
83 if (std::regex_match(std::string(str), std::regex(pattern)))
84 return 1;
85 default:
86 break;
87 }
54 88
55 const auto found = str.find_first_not_of(chars); 89 return animone_internal_util_EqualStrings(pattern, str);
56
57 if (found == 0)
58 return false;
59
60 if (found == std::string::npos)
61 str.clear();
62 else
63 str.erase(0, found);
64
65 return true;
66 } 90 }
67 91
68 bool TrimRight(std::string& str, const char* chars) { 92 /* Modifies `str` inplace */
69 if (str.empty()) 93 int animone_internal_util_TrimLeft(char *str, const char* chars)
70 return false; 94 {
95 if (!str || !*str)
96 return 0;
71 97
72 const auto found = str.find_last_not_of(chars); 98 const size_t found = strcspn(str, chars);
99 const size_t len = strlen(str);
73 100
74 if (found == str.size() - 1) 101 if (found == len)
75 return false; 102 return 1; // nothing to do
76 103
77 if (found == std::string::npos) 104 memmove(str, str + found, len - found);
78 str.clear(); 105 str[len - found] = '\0';
79 else
80 str.resize(found + 1);
81 106
82 return true; 107 return 1;
83 } 108 }
84 109
85 } // namespace animone::internal::util 110 // reverse version of strcspn
111 static inline size_t _animone_internal_strrcspn(const char *str1, const char *str2)
112 {
113 const size_t str1len = strlen(str1);
114 ptrdiff_t i; /* FIXME: this should be using size_t */
115
116 for (i = str1len - 1; i >= 0; i--) {
117 size_t found = strcspn(str1 + i, str2);
118 if (found != str1len - i)
119 return i;
120 }
121
122 return str1len;
123 }
124
125 int animone_internal_util_TrimRight(char *str, const char* chars)
126 {
127 if (!str || !*str)
128 return 0;
129
130 // We can get away without really moving memory around here.
131 // The old version simply used std::string::find_last_not_of,
132 // which I'm fairly sure isn't the intention of this function
133 // (for example find_last_not_of("gb ") returns "gb ").
134 const size_t found = _animone_internal_strrcspn(str, chars);
135 str[found] = '\0';
136
137 return 1;
138 }