1 /** @file strutil.c String and text utilities.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de/strutil.h"
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #if WIN32
29 # define strcasecmp _stricmp
30 #else
31 # include <strings.h>
32 #endif
33
dd_vsnprintf(char * str,size_t size,char const * format,va_list ap)34 int dd_vsnprintf(char *str, size_t size, char const *format, va_list ap)
35 {
36 int result = vsnprintf(str, size, format, ap);
37
38 #ifdef WIN32
39 // Always terminate.
40 str[size - 1] = 0;
41 return result;
42 #else
43 return result >= (int)size? -1 : (int)size;
44 #endif
45 }
46
dd_snprintf(char * str,size_t size,char const * format,...)47 int dd_snprintf(char *str, size_t size, char const *format, ...)
48 {
49 int result = 0;
50
51 va_list args;
52 va_start(args, format);
53 result = dd_vsnprintf(str, size, format, args);
54 va_end(args);
55
56 return result;
57 }
58
59 #ifdef WIN32
strcasestr(const char * text,const char * sub)60 const char* strcasestr(const char *text, const char *sub)
61 {
62 int textLen = strlen(text);
63 int subLen = strlen(sub);
64 int i;
65
66 for (i = 0; i <= textLen - subLen; ++i)
67 {
68 const char* start = text + i;
69 if (!_strnicmp(start, sub, subLen)) return start;
70 }
71 return 0;
72 }
73 #endif
74
75 #ifdef UNIX
strupr(char * string)76 char* strupr(char* string)
77 {
78 char* ch = string;
79 for (; *ch; ch++) *ch = toupper(*ch);
80 return string;
81 }
strlwr(char * string)82 char* strlwr(char* string)
83 {
84 char* ch = string;
85 for (; *ch; ch++) *ch = tolower(*ch);
86 return string;
87 }
88 #endif // UNIX
89
M_SkipWhite(const char * str)90 char *M_SkipWhite(const char *str)
91 {
92 while (*str && DENG_ISSPACE(*str))
93 str++;
94 return (char *) str;
95 }
96
M_FindWhite(const char * str)97 char *M_FindWhite(const char *str)
98 {
99 while (*str && !DENG_ISSPACE(*str))
100 str++;
101 return (char *) str;
102 }
103
M_StripLeft(char * str)104 void M_StripLeft(char* str)
105 {
106 size_t len, num;
107 if (NULL == str || !str[0]) return;
108
109 len = strlen(str);
110 // Count leading whitespace characters.
111 num = 0;
112 while (num < len && isspace(str[num]))
113 ++num;
114 if (0 == num) return;
115
116 // Remove 'num' characters.
117 memmove(str, str + num, len - num);
118 str[len] = 0;
119 }
120
M_StripRight(char * str,size_t len)121 void M_StripRight(char* str, size_t len)
122 {
123 char* end;
124 int numZeroed = 0;
125 if (NULL == str || 0 == len) return;
126
127 end = str + strlen(str) - 1;
128 while (end >= str && isspace(*end))
129 {
130 end--;
131 numZeroed++;
132 }
133 memset(end + 1, 0, numZeroed);
134 }
135
M_Strip(char * str,size_t len)136 void M_Strip(char* str, size_t len)
137 {
138 M_StripLeft(str);
139 M_StripRight(str, len);
140 }
141
M_SkipLine(char * str)142 char* M_SkipLine(char* str)
143 {
144 while (*str && *str != '\n')
145 str++;
146 // If the newline was found, skip it, too.
147 if (*str == '\n')
148 str++;
149 return str;
150 }
151
M_IsStringValidInt(const char * str)152 dd_bool M_IsStringValidInt(const char* str)
153 {
154 size_t i, len;
155 const char* c;
156 dd_bool isBad;
157
158 if (!str)
159 return false;
160
161 len = strlen(str);
162 if (len == 0)
163 return false;
164
165 for (i = 0, c = str, isBad = false; i < len && !isBad; ++i, c++)
166 {
167 if (i != 0 && *c == '-')
168 isBad = true; // sign is in the wrong place.
169 else if (*c < '0' || *c > '9')
170 isBad = true; // non-numeric character.
171 }
172
173 return !isBad;
174 }
175
M_IsStringValidByte(const char * str)176 dd_bool M_IsStringValidByte(const char* str)
177 {
178 if (M_IsStringValidInt(str))
179 {
180 int val = atoi(str);
181 if (!(val < 0 || val > 255))
182 return true;
183 }
184 return false;
185 }
186
M_IsStringValidFloat(const char * str)187 dd_bool M_IsStringValidFloat(const char* str)
188 {
189 size_t i, len;
190 const char* c;
191 dd_bool isBad, foundDP = false;
192
193 if (!str)
194 return false;
195
196 len = strlen(str);
197 if (len == 0)
198 return false;
199
200 for (i = 0, c = str, isBad = false; i < len && !isBad; ++i, c++)
201 {
202 if (i != 0 && *c == '-')
203 isBad = true; // sign is in the wrong place.
204 else if (*c == '.')
205 {
206 if (foundDP)
207 isBad = true; // multiple decimal places??
208 else
209 foundDP = true;
210 }
211 else if (*c < '0' || *c > '9')
212 isBad = true; // other non-numeric character.
213 }
214
215 return !isBad;
216 }
217
M_StrCat(char * buf,const char * str,size_t bufSize)218 char* M_StrCat(char* buf, const char* str, size_t bufSize)
219 {
220 return M_StrnCat(buf, str, strlen(str), bufSize);
221 }
222
M_StrnCat(char * buf,const char * str,size_t nChars,size_t bufSize)223 char* M_StrnCat(char* buf, const char* str, size_t nChars, size_t bufSize)
224 {
225 int n = nChars;
226 int destLen = strlen(buf);
227 if ((int)bufSize - destLen - 1 < n)
228 {
229 // Cannot copy more than fits in the buffer.
230 // The 1 is for the null character.
231 n = bufSize - destLen - 1;
232 }
233 if (n <= 0) return buf; // No space left.
234 return strncat(buf, str, n);
235 }
236
M_StrCatQuoted(char * dest,const char * src,size_t len)237 char* M_StrCatQuoted(char* dest, const char* src, size_t len)
238 {
239 size_t k = strlen(dest) + 1, i;
240
241 strncat(dest, "\"", len);
242 for (i = 0; src[i]; i++)
243 {
244 if (src[i] == '"')
245 {
246 strncat(dest, "\\\"", len);
247 k += 2;
248 }
249 else
250 {
251 dest[k++] = src[i];
252 dest[k] = 0;
253 }
254 }
255 strncat(dest, "\"", len);
256
257 return dest;
258 }
259
M_LimitedStrCat(char * buf,const char * str,size_t maxWidth,char separator,size_t bufLength)260 char* M_LimitedStrCat(char* buf, const char* str, size_t maxWidth,
261 char separator, size_t bufLength)
262 {
263 dd_bool isEmpty = !buf[0];
264 size_t length;
265
266 // How long is this name?
267 length = MIN_OF(maxWidth, strlen(str));
268
269 // A separator is included if this is not the first name.
270 if (separator && !isEmpty)
271 ++length;
272
273 // Does it fit?
274 if (strlen(buf) + length < bufLength)
275 {
276 if (separator && !isEmpty)
277 {
278 char sepBuf[2];
279
280 sepBuf[0] = separator;
281 sepBuf[1] = 0;
282
283 strcat(buf, sepBuf);
284 }
285 strncat(buf, str, length);
286 }
287
288 return buf;
289 }
290
M_ForceUppercase(char * text)291 void M_ForceUppercase(char *text)
292 {
293 char c;
294
295 while ((c = *text) != 0)
296 {
297 if (c >= 'a' && c <= 'z')
298 {
299 *text++ = c - ('a' - 'A');
300 }
301 else
302 {
303 text++;
304 }
305 }
306 }
307
M_StrTok(char ** cursor,const char * delimiters)308 char* M_StrTok(char** cursor, const char* delimiters)
309 {
310 char* begin = *cursor;
311
312 while (**cursor && !strchr(delimiters, **cursor))
313 (*cursor)++;
314
315 if (**cursor)
316 {
317 // Stop here.
318 **cursor = 0;
319
320 // Advance one more so we'll start from the right character on
321 // the next call.
322 (*cursor)++;
323 }
324
325 return begin;
326 }
327
M_TrimmedFloat(float val)328 char* M_TrimmedFloat(float val)
329 {
330 static char trimmedFloatBuffer[32];
331 char* ptr = trimmedFloatBuffer;
332
333 sprintf(ptr, "%f", val);
334 // Get rid of the extra zeros.
335 for (ptr += strlen(ptr) - 1; ptr >= trimmedFloatBuffer; ptr--)
336 {
337 if (*ptr == '0')
338 *ptr = 0;
339 else if (*ptr == '.')
340 {
341 *ptr = 0;
342 break;
343 }
344 else
345 break;
346 }
347 return trimmedFloatBuffer;
348 }
349