Line | Count | Source |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 2008-2025 The ProFTPD Project team |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or modify |
6 | | * it under the terms of the GNU General Public License as published by |
7 | | * the Free Software Foundation; either version 2 of the License, or |
8 | | * (at your option) any later version. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License |
16 | | * along with this program; if not, write to the Free Software |
17 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. |
18 | | * |
19 | | * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu |
20 | | * and other respective copyright holders give permission to link this program |
21 | | * with OpenSSL, and distribute the resulting executable, without including |
22 | | * the source code for OpenSSL in the source distribution. |
23 | | */ |
24 | | |
25 | | /* String manipulation functions. */ |
26 | | |
27 | | #include "conf.h" |
28 | | |
29 | | /* Maximum number of matches that we will do in a given string. */ |
30 | 0 | #define PR_STR_MAX_MATCHES 128 |
31 | | |
32 | | static const char *str_vreplace(pool *p, unsigned int max_replaces, |
33 | 0 | const char *s, va_list args) { |
34 | 0 | const char *src; |
35 | 0 | char *m, *r, *cp; |
36 | 0 | char *matches[PR_STR_MAX_MATCHES+1], *replaces[PR_STR_MAX_MATCHES+1]; |
37 | 0 | char buf[PR_TUNABLE_PATH_MAX] = {'\0'}, *pbuf = NULL; |
38 | 0 | size_t nmatches = 0, rlen = 0; |
39 | 0 | int blen = 0; |
40 | |
|
41 | 0 | src = s; |
42 | 0 | cp = buf; |
43 | 0 | *cp = '\0'; |
44 | |
|
45 | 0 | memset(matches, 0, sizeof(matches)); |
46 | 0 | memset(replaces, 0, sizeof(replaces)); |
47 | |
|
48 | 0 | blen = strlen(src) + 1; |
49 | |
|
50 | 0 | while ((m = va_arg(args, char *)) != NULL && |
51 | 0 | nmatches < PR_STR_MAX_MATCHES) { |
52 | 0 | char *tmp = NULL; |
53 | 0 | unsigned int count = 0; |
54 | |
|
55 | 0 | r = va_arg(args, char *); |
56 | 0 | if (r == NULL) { |
57 | 0 | break; |
58 | 0 | } |
59 | | |
60 | | /* Increase the length of the needed buffer by the difference between |
61 | | * the given match and replacement strings, multiplied by the number |
62 | | * of times the match string occurs in the source string. |
63 | | */ |
64 | 0 | tmp = strstr(s, m); |
65 | 0 | while (tmp) { |
66 | 0 | pr_signals_handle(); |
67 | 0 | count++; |
68 | 0 | if (count > max_replaces) { |
69 | 0 | errno = E2BIG; |
70 | 0 | return NULL; |
71 | 0 | } |
72 | | |
73 | | /* Be sure to increment the pointer returned by strstr(3), to |
74 | | * advance past the beginning of the substring for which we are |
75 | | * looking. Otherwise, we just loop endlessly, seeing the same |
76 | | * value for tmp over and over. |
77 | | */ |
78 | 0 | tmp += strlen(m); |
79 | 0 | tmp = strstr(tmp, m); |
80 | 0 | } |
81 | | |
82 | | /* We are only concerned about match/replacement strings that actually |
83 | | * occur in the given string. |
84 | | */ |
85 | 0 | if (count) { |
86 | 0 | blen += count * (strlen(r) - strlen(m)); |
87 | 0 | if (blen < 0) { |
88 | | /* Integer overflow. In order to overflow this, somebody must be |
89 | | * doing something very strange. The possibility still exists that |
90 | | * we might not catch this overflow in extreme corner cases, but |
91 | | * massive amounts of data (gigabytes) would need to be in s to |
92 | | * trigger this, easily larger than any buffer we might use. |
93 | | */ |
94 | 0 | return s; |
95 | 0 | } |
96 | 0 | matches[nmatches] = m; |
97 | 0 | replaces[nmatches++] = r; |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | /* If there are no matches, then there is nothing to replace. */ |
102 | 0 | if (nmatches == 0) { |
103 | 0 | return s; |
104 | 0 | } |
105 | | |
106 | | /* Try to handle large buffer situations (i.e. escaping of PR_TUNABLE_PATH_MAX |
107 | | * (>2048) correctly, but do not allow very big buffer sizes, that may |
108 | | * be dangerous (BUFSIZ may be defined in stdio.h) in some library |
109 | | * functions. |
110 | | */ |
111 | | #ifndef BUFSIZ |
112 | | # define BUFSIZ 8192 |
113 | | #endif |
114 | | |
115 | 0 | if (blen >= BUFSIZ) { |
116 | 0 | errno = ENOSPC; |
117 | 0 | return NULL; |
118 | 0 | } |
119 | | |
120 | 0 | cp = pbuf = (char *) pcalloc(p, ++blen); |
121 | |
|
122 | 0 | while (*src) { |
123 | 0 | char **mptr, **rptr; |
124 | |
|
125 | 0 | for (mptr = matches, rptr = replaces; *mptr; mptr++, rptr++) { |
126 | 0 | size_t mlen; |
127 | |
|
128 | 0 | pr_signals_handle(); |
129 | |
|
130 | 0 | mlen = strlen(*mptr); |
131 | 0 | rlen = strlen(*rptr); |
132 | |
|
133 | 0 | if (strncmp(src, *mptr, mlen) == 0) { |
134 | 0 | sstrncpy(cp, *rptr, blen - strlen(pbuf)); |
135 | |
|
136 | 0 | if (((cp + rlen) - pbuf + 1) > blen) { |
137 | 0 | pr_log_pri(PR_LOG_ERR, |
138 | 0 | "WARNING: attempt to overflow internal ProFTPD buffers"); |
139 | 0 | cp = pbuf; |
140 | |
|
141 | 0 | cp += (blen - 1); |
142 | 0 | goto done; |
143 | |
|
144 | 0 | } else { |
145 | 0 | cp += rlen; |
146 | 0 | } |
147 | | |
148 | 0 | src += mlen; |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | 0 | if (!*mptr) { |
154 | 0 | if ((cp - pbuf + 1) >= blen) { |
155 | 0 | pr_log_pri(PR_LOG_ERR, |
156 | 0 | "WARNING: attempt to overflow internal ProFTPD buffers"); |
157 | 0 | cp = pbuf; |
158 | |
|
159 | 0 | cp += (blen - 1); |
160 | 0 | goto done; |
161 | 0 | } |
162 | | |
163 | 0 | *cp++ = *src++; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | done: |
168 | 0 | *cp = '\0'; |
169 | |
|
170 | 0 | return pbuf; |
171 | 0 | } |
172 | | |
173 | 0 | const char *pr_str_quote(pool *p, const char *str) { |
174 | 0 | if (p == NULL || |
175 | 0 | str == NULL) { |
176 | 0 | errno = EINVAL; |
177 | 0 | return NULL; |
178 | 0 | } |
179 | | |
180 | 0 | return sreplace(p, str, "\"", "\"\"", NULL); |
181 | 0 | } |
182 | | |
183 | 0 | const char *quote_dir(pool *p, char *path) { |
184 | 0 | return pr_str_quote(p, path); |
185 | 0 | } |
186 | | |
187 | | const char *pr_str_replace(pool *p, unsigned int max_replaces, |
188 | 0 | const char *s, ...) { |
189 | 0 | va_list args; |
190 | 0 | const char *res = NULL; |
191 | |
|
192 | 0 | if (p == NULL || |
193 | 0 | s == NULL || |
194 | 0 | max_replaces == 0) { |
195 | 0 | errno = EINVAL; |
196 | 0 | return NULL; |
197 | 0 | } |
198 | | |
199 | 0 | va_start(args, s); |
200 | 0 | res = str_vreplace(p, max_replaces, s, args); |
201 | 0 | va_end(args); |
202 | |
|
203 | 0 | return res; |
204 | 0 | } |
205 | | |
206 | 0 | const char *sreplace(pool *p, const char *s, ...) { |
207 | 0 | va_list args; |
208 | 0 | const char *res = NULL; |
209 | |
|
210 | 0 | if (p == NULL || |
211 | 0 | s == NULL) { |
212 | 0 | errno = EINVAL; |
213 | 0 | return NULL; |
214 | 0 | } |
215 | | |
216 | 0 | va_start(args, s); |
217 | 0 | res = str_vreplace(p, PR_STR_MAX_REPLACEMENTS, s, args); |
218 | 0 | va_end(args); |
219 | |
|
220 | 0 | if (res == NULL && |
221 | 0 | errno == E2BIG) { |
222 | | /* For backward compatible behavior. */ |
223 | 0 | return s; |
224 | 0 | } |
225 | | |
226 | 0 | return res; |
227 | 0 | } |
228 | | |
229 | | /* "safe" strcat, saves room for NUL at end of dst, and refuses to copy more |
230 | | * than "n" bytes. |
231 | | */ |
232 | 0 | char *sstrcat(char *dst, const char *src, size_t n) { |
233 | 0 | register char *d = dst; |
234 | |
|
235 | 0 | if (dst == NULL || |
236 | 0 | src == NULL || |
237 | 0 | n == 0) { |
238 | 0 | errno = EINVAL; |
239 | 0 | return NULL; |
240 | 0 | } |
241 | | |
242 | | /* Edge case short circuit; strlcat(3) doesn't do what I think it should |
243 | | * do for this particular case. |
244 | | */ |
245 | 0 | if (n > 1) { |
246 | | #if defined(HAVE_STRLCAT) |
247 | | strlcat(dst, src, n); |
248 | | |
249 | | #else |
250 | 0 | for (; *d && n > 1; d++, n--) { |
251 | 0 | } |
252 | |
|
253 | 0 | while (n-- > 1 && *src) { |
254 | 0 | *d++ = *src++; |
255 | 0 | } |
256 | |
|
257 | 0 | *d = '\0'; |
258 | 0 | #endif /* HAVE_STRLCAT */ |
259 | |
|
260 | 0 | } else { |
261 | 0 | *d = '\0'; |
262 | 0 | } |
263 | |
|
264 | 0 | return dst; |
265 | 0 | } |
266 | | |
267 | 0 | char *pstrdup(pool *p, const char *str) { |
268 | 0 | char *res; |
269 | 0 | size_t len; |
270 | |
|
271 | 0 | if (p == NULL || |
272 | 0 | str == NULL) { |
273 | 0 | errno = EINVAL; |
274 | 0 | return NULL; |
275 | 0 | } |
276 | | |
277 | 0 | len = strlen(str) + 1; |
278 | |
|
279 | 0 | res = palloc(p, len); |
280 | 0 | if (res != NULL) { |
281 | 0 | sstrncpy(res, str, len); |
282 | 0 | } |
283 | |
|
284 | 0 | return res; |
285 | 0 | } |
286 | | |
287 | 0 | char *pstrndup(pool *p, const char *str, size_t n) { |
288 | 0 | char *res; |
289 | |
|
290 | 0 | if (!p || !str) { |
291 | 0 | errno = EINVAL; |
292 | 0 | return NULL; |
293 | 0 | } |
294 | | |
295 | 0 | res = palloc(p, n + 1); |
296 | 0 | sstrncpy(res, str, n + 1); |
297 | 0 | return res; |
298 | 0 | } |
299 | | |
300 | 0 | char *pdircat(pool *p, ...) { |
301 | 0 | char *argp, *ptr, *res; |
302 | 0 | char last; |
303 | 0 | int count = 0; |
304 | 0 | size_t len = 0, res_len = 0; |
305 | 0 | va_list ap; |
306 | |
|
307 | 0 | if (p == NULL) { |
308 | 0 | errno = EINVAL; |
309 | 0 | return NULL; |
310 | 0 | } |
311 | | |
312 | 0 | va_start(ap, p); |
313 | |
|
314 | 0 | last = 0; |
315 | |
|
316 | 0 | while ((res = va_arg(ap, char *)) != NULL) { |
317 | | /* If the first argument is "", we have to account for a leading / |
318 | | * which must be added. |
319 | | */ |
320 | 0 | if (!count++ && !*res) { |
321 | 0 | len++; |
322 | |
|
323 | 0 | } else if (last && last != '/' && *res != '/') { |
324 | 0 | len++; |
325 | |
|
326 | 0 | } else if (last && last == '/' && *res == '/') { |
327 | 0 | len--; |
328 | 0 | } |
329 | |
|
330 | 0 | res_len = strlen(res); |
331 | 0 | len += res_len; |
332 | 0 | last = (*res ? res[res_len-1] : 0); |
333 | 0 | } |
334 | |
|
335 | 0 | va_end(ap); |
336 | 0 | ptr = res = (char *) pcalloc(p, len + 1); |
337 | |
|
338 | 0 | va_start(ap, p); |
339 | |
|
340 | 0 | last = res_len = 0; |
341 | |
|
342 | 0 | while ((argp = va_arg(ap, char *)) != NULL) { |
343 | 0 | size_t arglen; |
344 | |
|
345 | 0 | if (last && last == '/' && *argp == '/') { |
346 | 0 | argp++; |
347 | |
|
348 | 0 | } else if (last && last != '/' && *argp != '/') { |
349 | 0 | sstrcat(ptr, "/", len + 1); |
350 | 0 | ptr += 1; |
351 | 0 | res_len += 1; |
352 | 0 | } |
353 | |
|
354 | 0 | arglen = strlen(argp); |
355 | 0 | sstrcat(ptr, argp, len + 1); |
356 | 0 | ptr += arglen; |
357 | 0 | res_len += arglen; |
358 | |
|
359 | 0 | last = (*res ? res[res_len-1] : 0); |
360 | 0 | } |
361 | |
|
362 | 0 | va_end(ap); |
363 | |
|
364 | 0 | return res; |
365 | 0 | } |
366 | | |
367 | 0 | char *pstrcat(pool *p, ...) { |
368 | 0 | char *argp, *ptr, *res; |
369 | 0 | size_t len = 0; |
370 | 0 | va_list ap; |
371 | |
|
372 | 0 | if (p == NULL) { |
373 | 0 | errno = EINVAL; |
374 | 0 | return NULL; |
375 | 0 | } |
376 | | |
377 | 0 | va_start(ap, p); |
378 | |
|
379 | 0 | while ((res = va_arg(ap, char *)) != NULL) { |
380 | 0 | len += strlen(res); |
381 | 0 | } |
382 | |
|
383 | 0 | va_end(ap); |
384 | |
|
385 | 0 | ptr = res = pcalloc(p, len + 1); |
386 | |
|
387 | 0 | va_start(ap, p); |
388 | |
|
389 | 0 | while ((argp = va_arg(ap, char *)) != NULL) { |
390 | 0 | size_t arglen; |
391 | |
|
392 | 0 | arglen = strlen(argp); |
393 | 0 | sstrcat(ptr, argp, len + 1); |
394 | 0 | ptr += arglen; |
395 | 0 | } |
396 | |
|
397 | 0 | va_end(ap); |
398 | |
|
399 | 0 | return res; |
400 | 0 | } |
401 | | |
402 | | int pr_strnrstr(const char *s, size_t slen, const char *suffix, |
403 | 0 | size_t suffixlen, int flags) { |
404 | 0 | int res = FALSE; |
405 | |
|
406 | 0 | if (s == NULL || |
407 | 0 | suffix == NULL) { |
408 | 0 | errno = EINVAL; |
409 | 0 | return -1; |
410 | 0 | } |
411 | | |
412 | 0 | if (slen == 0) { |
413 | 0 | slen = strlen(s); |
414 | 0 | } |
415 | |
|
416 | 0 | if (suffixlen == 0) { |
417 | 0 | suffixlen = strlen(suffix); |
418 | 0 | } |
419 | |
|
420 | 0 | if (slen == 0 && |
421 | 0 | suffixlen == 0) { |
422 | 0 | return TRUE; |
423 | 0 | } |
424 | | |
425 | 0 | if (slen == 0 || |
426 | 0 | suffixlen == 0) { |
427 | 0 | return FALSE; |
428 | 0 | } |
429 | | |
430 | 0 | if (suffixlen > slen) { |
431 | 0 | return FALSE; |
432 | 0 | } |
433 | | |
434 | 0 | if (flags & PR_STR_FL_IGNORE_CASE) { |
435 | 0 | if (strncasecmp(s + (slen - suffixlen), suffix, suffixlen) == 0) { |
436 | 0 | res = TRUE; |
437 | 0 | } |
438 | |
|
439 | 0 | } else { |
440 | 0 | if (strncmp(s + (slen - suffixlen), suffix, suffixlen) == 0) { |
441 | 0 | res = TRUE; |
442 | 0 | } |
443 | 0 | } |
444 | |
|
445 | 0 | return res; |
446 | 0 | } |
447 | | |
448 | 0 | const char *pr_str_strip(pool *p, const char *str) { |
449 | 0 | const char *dup_str, *start, *finish; |
450 | 0 | size_t len = 0; |
451 | |
|
452 | 0 | if (p == NULL || |
453 | 0 | str == NULL) { |
454 | 0 | errno = EINVAL; |
455 | 0 | return NULL; |
456 | 0 | } |
457 | | |
458 | | /* First, find the non-whitespace start of the given string */ |
459 | 0 | for (start = str; PR_ISSPACE(*start); start++) { |
460 | 0 | } |
461 | | |
462 | | /* Now, find the non-whitespace end of the given string */ |
463 | 0 | for (finish = &str[strlen(str)-1]; PR_ISSPACE(*finish); finish--) { |
464 | 0 | } |
465 | | |
466 | | /* Include for the last byte, of course. */ |
467 | 0 | len = finish - start + 1; |
468 | | |
469 | | /* The space-stripped string is, then, everything from start to finish. */ |
470 | 0 | dup_str = pstrndup(p, start, len); |
471 | |
|
472 | 0 | return dup_str; |
473 | 0 | } |
474 | | |
475 | 0 | char *pr_str_strip_end(char *s, const char *ch) { |
476 | 0 | size_t len; |
477 | |
|
478 | 0 | if (s == NULL || |
479 | 0 | ch == NULL) { |
480 | 0 | errno = EINVAL; |
481 | 0 | return NULL; |
482 | 0 | } |
483 | | |
484 | 0 | len = strlen(s); |
485 | |
|
486 | 0 | while (len && strchr(ch, *(s+len - 1))) { |
487 | 0 | pr_signals_handle(); |
488 | |
|
489 | 0 | *(s+len - 1) = '\0'; |
490 | 0 | len--; |
491 | 0 | } |
492 | |
|
493 | 0 | return s; |
494 | 0 | } |
495 | | |
496 | | #if defined(HAVE_STRTOULL) && \ |
497 | | (SIZEOF_UID_T == SIZEOF_LONG_LONG && SIZEOF_GID_T == SIZEOF_LONG_LONG) |
498 | | static int parse_ull(const char *val, unsigned long long *num) { |
499 | | char *endp = NULL; |
500 | | unsigned long long res; |
501 | | |
502 | | res = strtoull(val, &endp, 10); |
503 | | if (endp && *endp) { |
504 | | errno = EINVAL; |
505 | | return -1; |
506 | | } |
507 | | |
508 | | *num = res; |
509 | | return 0; |
510 | | } |
511 | | #endif /* HAVE_STRTOULL */ |
512 | | |
513 | 0 | static int parse_ul(const char *val, unsigned long *num) { |
514 | 0 | char *endp = NULL; |
515 | 0 | unsigned long res; |
516 | |
|
517 | 0 | res = strtoul(val, &endp, 10); |
518 | 0 | if (endp && *endp) { |
519 | 0 | errno = EINVAL; |
520 | 0 | return -1; |
521 | 0 | } |
522 | | |
523 | 0 | *num = res; |
524 | 0 | return 0; |
525 | 0 | } |
526 | | |
527 | 0 | char *pr_str_bin2hex(pool *p, const unsigned char *buf, size_t len, int flags) { |
528 | 0 | static const char *hex_lc = "0123456789abcdef", *hex_uc = "0123456789ABCDEF"; |
529 | 0 | register unsigned int i; |
530 | 0 | const char *hex_vals; |
531 | 0 | char *hex, *ptr; |
532 | 0 | size_t hex_len; |
533 | |
|
534 | 0 | if (p == NULL || |
535 | 0 | buf == NULL) { |
536 | 0 | errno = EINVAL; |
537 | 0 | return NULL; |
538 | 0 | } |
539 | | |
540 | 0 | if (len == 0) { |
541 | 0 | return pstrdup(p, ""); |
542 | 0 | } |
543 | | |
544 | | /* By default, we use lowercase hex values. */ |
545 | 0 | hex_vals = hex_lc; |
546 | 0 | if (flags & PR_STR_FL_HEX_USE_UC) { |
547 | 0 | hex_vals = hex_uc; |
548 | 0 | } |
549 | |
|
550 | 0 | hex_len = (len * 2) + 1; |
551 | 0 | hex = palloc(p, hex_len); |
552 | |
|
553 | 0 | ptr = hex; |
554 | 0 | for (i = 0; i < len; i++) { |
555 | 0 | *ptr++ = hex_vals[buf[i] >> 4]; |
556 | 0 | *ptr++ = hex_vals[buf[i] % 16]; |
557 | 0 | } |
558 | 0 | *ptr = '\0'; |
559 | |
|
560 | 0 | return hex; |
561 | 0 | } |
562 | | |
563 | 0 | static int c2h(char c, unsigned char *h) { |
564 | 0 | if (c >= '0' && |
565 | 0 | c <= '9') { |
566 | 0 | *h = c - '0'; |
567 | 0 | return TRUE; |
568 | 0 | } |
569 | | |
570 | 0 | if (c >= 'a' && |
571 | 0 | c <= 'f') { |
572 | 0 | *h = c - 'a' + 10; |
573 | 0 | return TRUE; |
574 | 0 | } |
575 | | |
576 | 0 | if (c >= 'A' && |
577 | 0 | c <= 'F') { |
578 | 0 | *h = c - 'A' + 10; |
579 | 0 | return TRUE; |
580 | 0 | } |
581 | | |
582 | 0 | return FALSE; |
583 | 0 | } |
584 | | |
585 | | unsigned char *pr_str_hex2bin(pool *p, const unsigned char *hex, size_t hex_len, |
586 | 0 | size_t *len) { |
587 | 0 | register unsigned int i, j; |
588 | 0 | unsigned char *data; |
589 | 0 | size_t data_len; |
590 | |
|
591 | 0 | if (p == NULL || |
592 | 0 | hex == NULL) { |
593 | 0 | errno = EINVAL; |
594 | 0 | return NULL; |
595 | 0 | } |
596 | | |
597 | 0 | if (hex_len == 0) { |
598 | 0 | hex_len = strlen((char *) hex); |
599 | 0 | } |
600 | |
|
601 | 0 | if (hex_len == 0) { |
602 | 0 | data = (unsigned char *) pstrdup(p, ""); |
603 | 0 | return data; |
604 | 0 | } |
605 | | |
606 | 0 | data_len = hex_len / 2; |
607 | 0 | data = palloc(p, data_len); |
608 | |
|
609 | 0 | for (i = 0, j = 0; i < hex_len; i += 2) { |
610 | 0 | unsigned char v1, v2; |
611 | |
|
612 | 0 | if (c2h(hex[i], &v1) == FALSE) { |
613 | 0 | errno = ERANGE; |
614 | 0 | return NULL; |
615 | 0 | } |
616 | | |
617 | 0 | if (c2h(hex[i+1], &v2) == FALSE) { |
618 | 0 | errno = ERANGE; |
619 | 0 | return NULL; |
620 | 0 | } |
621 | | |
622 | 0 | data[j++] = ((v1 << 4) | v2); |
623 | 0 | } |
624 | | |
625 | 0 | if (len != NULL) { |
626 | 0 | *len = data_len; |
627 | 0 | } |
628 | |
|
629 | 0 | return data; |
630 | 0 | } |
631 | | |
632 | | /* Calculate the Damerau-Levenshtein distance between strings `a' and `b'. |
633 | | * This implementation borrows from the git implementation; see |
634 | | * git/src/levenshtein.c. |
635 | | */ |
636 | | int pr_str_levenshtein(pool *p, const char *a, const char *b, int swap_cost, |
637 | 0 | int subst_cost, int insert_cost, int del_cost, int flags) { |
638 | 0 | size_t alen, blen; |
639 | 0 | unsigned int i, j; |
640 | 0 | int *row0, *row1, *row2, res; |
641 | 0 | pool *tmp_pool; |
642 | |
|
643 | 0 | if (p == NULL || |
644 | 0 | a == NULL || |
645 | 0 | b == NULL) { |
646 | 0 | errno = EINVAL; |
647 | 0 | return -1; |
648 | 0 | } |
649 | | |
650 | 0 | alen = strlen(a); |
651 | 0 | blen = strlen(b); |
652 | |
|
653 | 0 | tmp_pool = make_sub_pool(p); |
654 | 0 | pr_pool_tag(tmp_pool, "Levenshtein Distance pool"); |
655 | |
|
656 | 0 | if (flags & PR_STR_FL_IGNORE_CASE) { |
657 | 0 | char *a2, *b2; |
658 | |
|
659 | 0 | a2 = pstrdup(tmp_pool, a); |
660 | 0 | for (i = 0; i < alen; i++) { |
661 | 0 | a2[i] = tolower((int) a[i]); |
662 | 0 | } |
663 | |
|
664 | 0 | b2 = pstrdup(tmp_pool, b); |
665 | 0 | for (i = 0; i < blen; i++) { |
666 | 0 | b2[i] = tolower((int) b[i]); |
667 | 0 | } |
668 | |
|
669 | 0 | a = a2; |
670 | 0 | b = b2; |
671 | 0 | } |
672 | |
|
673 | 0 | row0 = pcalloc(tmp_pool, sizeof(int) * (blen + 1)); |
674 | 0 | row1 = pcalloc(tmp_pool, sizeof(int) * (blen + 1)); |
675 | 0 | row2 = pcalloc(tmp_pool, sizeof(int) * (blen + 1)); |
676 | |
|
677 | 0 | for (j = 0; j <= blen; j++) { |
678 | 0 | row1[j] = j * insert_cost; |
679 | 0 | } |
680 | |
|
681 | 0 | for (i = 0; i < alen; i++) { |
682 | 0 | int *ptr; |
683 | |
|
684 | 0 | row2[0] = (i + 1) * del_cost; |
685 | 0 | for (j = 0; j < blen; j++) { |
686 | | /* Substitution */ |
687 | 0 | row2[j + 1] = row1[j] + (subst_cost * (a[i] != b[j])); |
688 | | |
689 | | /* Swap */ |
690 | 0 | if (i > 0 && |
691 | 0 | j > 0 && |
692 | 0 | a[i-1] == b[j] && |
693 | 0 | a[i] == b[j-1] && |
694 | 0 | row2[j+1] > (row0[j-1] + swap_cost)) { |
695 | 0 | row2[j+1] = row0[j-1] + swap_cost; |
696 | 0 | } |
697 | | |
698 | | /* Deletion */ |
699 | 0 | if (row2[j+1] > (row1[j+1] + del_cost)) { |
700 | 0 | row2[j+1] = row1[j+1] + del_cost; |
701 | 0 | } |
702 | | |
703 | | /* Insertion */ |
704 | 0 | if (row2[j+1] > (row2[j] + insert_cost)) { |
705 | 0 | row2[j+1] = row2[j] + insert_cost; |
706 | 0 | } |
707 | 0 | } |
708 | |
|
709 | 0 | ptr = row0; |
710 | 0 | row0 = row1; |
711 | 0 | row1 = row2; |
712 | 0 | row2 = ptr; |
713 | 0 | } |
714 | |
|
715 | 0 | res = row2[blen]; |
716 | |
|
717 | 0 | destroy_pool(tmp_pool); |
718 | 0 | return res; |
719 | 0 | } |
720 | | |
721 | | /* For tracking the Levenshtein distance for a string. */ |
722 | | struct candidate { |
723 | | const char *s; |
724 | | int distance; |
725 | | int flags; |
726 | | }; |
727 | | |
728 | 0 | static int distance_cmp(const void *a, const void *b) { |
729 | 0 | const struct candidate *cand1, *cand2; |
730 | 0 | const char *s1, *s2; |
731 | 0 | int distance1, distance2; |
732 | |
|
733 | 0 | cand1 = *((const struct candidate **) a); |
734 | 0 | s1 = cand1->s; |
735 | 0 | distance1 = cand1->distance; |
736 | |
|
737 | 0 | cand2 = *((const struct candidate **) b); |
738 | 0 | s2 = cand2->s; |
739 | 0 | distance2 = cand2->distance; |
740 | |
|
741 | 0 | if (distance1 != distance2) { |
742 | 0 | return distance1 - distance2; |
743 | 0 | } |
744 | | |
745 | 0 | if (cand1->flags & PR_STR_FL_IGNORE_CASE) { |
746 | 0 | return strcasecmp(s1, s2); |
747 | 0 | } |
748 | | |
749 | 0 | return strcmp(s1, s2); |
750 | 0 | } |
751 | | |
752 | | array_header *pr_str_get_similars(pool *p, const char *s, |
753 | 0 | array_header *candidates, int max_distance, int flags) { |
754 | 0 | register unsigned int i; |
755 | 0 | size_t len; |
756 | 0 | array_header *similars; |
757 | 0 | struct candidate **distances; |
758 | 0 | pool *tmp_pool; |
759 | |
|
760 | 0 | if (p == NULL || |
761 | 0 | s == NULL || |
762 | 0 | candidates == NULL) { |
763 | 0 | errno = EINVAL; |
764 | 0 | return NULL; |
765 | 0 | } |
766 | | |
767 | 0 | if (candidates->nelts == 0) { |
768 | 0 | errno = ENOENT; |
769 | 0 | return NULL; |
770 | 0 | } |
771 | | |
772 | 0 | if (max_distance <= 0) { |
773 | 0 | max_distance = PR_STR_DEFAULT_MAX_EDIT_DISTANCE; |
774 | 0 | } |
775 | |
|
776 | 0 | tmp_pool = make_sub_pool(p); |
777 | 0 | pr_pool_tag(tmp_pool, "Similar Strings pool"); |
778 | | |
779 | | /* In order to use qsort(3), we need a contiguous block of memory, not |
780 | | * one of our array_headers. |
781 | | */ |
782 | |
|
783 | 0 | distances = pcalloc(tmp_pool, candidates->nelts * sizeof(struct candidate *)); |
784 | |
|
785 | 0 | len = strlen(s); |
786 | 0 | for (i = 0; i < candidates->nelts; i++) { |
787 | 0 | const char *c; |
788 | 0 | struct candidate *cand; |
789 | 0 | int prefix_match = FALSE; |
790 | |
|
791 | 0 | c = ((const char **) candidates->elts)[i]; |
792 | 0 | cand = pcalloc(tmp_pool, sizeof(struct candidate)); |
793 | 0 | cand->s = c; |
794 | 0 | cand->flags = flags; |
795 | | |
796 | | /* Give prefix matches a higher score */ |
797 | 0 | if (flags & PR_STR_FL_IGNORE_CASE) { |
798 | 0 | if (strncasecmp(c, s, len) == 0) { |
799 | 0 | prefix_match = TRUE; |
800 | 0 | } |
801 | |
|
802 | 0 | } else { |
803 | 0 | if (strncmp(c, s, len) == 0) { |
804 | 0 | prefix_match = TRUE; |
805 | 0 | } |
806 | 0 | } |
807 | |
|
808 | 0 | if (prefix_match == TRUE) { |
809 | 0 | cand->distance = 0; |
810 | |
|
811 | 0 | } else { |
812 | | /* Note: We arbitrarily add one to the edit distance, in order to |
813 | | * distinguish a distance of zero from our prefix match "distances" of |
814 | | * zero above. |
815 | | */ |
816 | 0 | cand->distance = pr_str_levenshtein(tmp_pool, s, c, 0, 2, 1, 3, |
817 | 0 | flags) + 1; |
818 | 0 | } |
819 | |
|
820 | 0 | distances[i] = cand; |
821 | 0 | } |
822 | |
|
823 | 0 | qsort(distances, candidates->nelts, sizeof(struct candidate *), distance_cmp); |
824 | |
|
825 | 0 | similars = make_array(p, candidates->nelts, sizeof(const char *)); |
826 | 0 | for (i = 0; i < candidates->nelts; i++) { |
827 | 0 | struct candidate *cand; |
828 | |
|
829 | 0 | cand = distances[i]; |
830 | 0 | if (cand->distance <= max_distance) { |
831 | 0 | *((const char **) push_array(similars)) = cand->s; |
832 | 0 | } |
833 | 0 | } |
834 | |
|
835 | 0 | destroy_pool(tmp_pool); |
836 | 0 | return similars; |
837 | 0 | } |
838 | | |
839 | 0 | array_header *pr_str_text_to_array(pool *p, const char *text, char delimiter) { |
840 | 0 | char *ptr; |
841 | 0 | array_header *items; |
842 | 0 | size_t text_len; |
843 | |
|
844 | 0 | if (p == NULL || |
845 | 0 | text == NULL) { |
846 | 0 | errno = EINVAL; |
847 | 0 | return NULL; |
848 | 0 | } |
849 | | |
850 | 0 | text_len = strlen(text); |
851 | 0 | items = make_array(p, 1, sizeof(char *)); |
852 | |
|
853 | 0 | if (text_len == 0) { |
854 | 0 | return items; |
855 | 0 | } |
856 | | |
857 | 0 | ptr = memchr(text, delimiter, text_len); |
858 | 0 | while (ptr != NULL) { |
859 | 0 | size_t item_len; |
860 | |
|
861 | 0 | pr_signals_handle(); |
862 | |
|
863 | 0 | item_len = ptr - text; |
864 | 0 | if (item_len > 0) { |
865 | 0 | char *item; |
866 | |
|
867 | 0 | item = palloc(p, item_len + 1); |
868 | 0 | memcpy(item, text, item_len); |
869 | 0 | item[item_len] = '\0'; |
870 | 0 | *((char **) push_array(items)) = item; |
871 | 0 | } |
872 | |
|
873 | 0 | text = ++ptr; |
874 | | |
875 | | /* Include one byte for the delimiter character being skipped over. */ |
876 | 0 | text_len = text_len - item_len - 1; |
877 | |
|
878 | 0 | if (text_len == 0) { |
879 | 0 | break; |
880 | 0 | } |
881 | | |
882 | 0 | ptr = memchr(text, delimiter, text_len); |
883 | 0 | } |
884 | |
|
885 | 0 | if (text_len > 0) { |
886 | 0 | *((char **) push_array(items)) = pstrdup(p, text); |
887 | 0 | } |
888 | |
|
889 | 0 | return items; |
890 | 0 | } |
891 | | |
892 | | char *pr_str_array_to_text(pool *p, const array_header *items, |
893 | 0 | const char *delimiter) { |
894 | 0 | register unsigned int i; |
895 | 0 | char **elts, *text = ""; |
896 | |
|
897 | 0 | if (p == NULL || |
898 | 0 | items == NULL || |
899 | 0 | delimiter == NULL) { |
900 | 0 | errno = EINVAL; |
901 | 0 | return NULL; |
902 | 0 | } |
903 | | |
904 | 0 | if (items->nelts == 0) { |
905 | 0 | return pstrdup(p, ""); |
906 | 0 | } |
907 | | |
908 | 0 | elts = items->elts; |
909 | 0 | for (i = 0; i < items->nelts; i++) { |
910 | 0 | char *elt; |
911 | |
|
912 | 0 | elt = elts[i]; |
913 | 0 | text = pstrcat(p, text, *text ? delimiter : "", elt, NULL); |
914 | 0 | } |
915 | |
|
916 | 0 | return text; |
917 | 0 | } |
918 | | |
919 | 0 | int pr_str2uid(const char *val, uid_t *uid) { |
920 | 0 | #ifdef HAVE_STRTOULL |
921 | 0 | unsigned long long ull = 0ULL; |
922 | 0 | #endif /* HAVE_STRTOULL */ |
923 | 0 | unsigned long ul = 0UL; |
924 | |
|
925 | 0 | if (val == NULL || |
926 | 0 | uid == NULL) { |
927 | 0 | errno = EINVAL; |
928 | 0 | return -1; |
929 | 0 | } |
930 | | |
931 | | #if SIZEOF_UID_T == SIZEOF_LONG_LONG |
932 | | # ifdef HAVE_STRTOULL |
933 | | if (parse_ull(val, &ull) < 0) { |
934 | | return -1; |
935 | | } |
936 | | *uid = ull; |
937 | | |
938 | | # else |
939 | | if (parse_ul(val, &ul) < 0) { |
940 | | return -1; |
941 | | } |
942 | | *uid = ul; |
943 | | # endif /* HAVE_STRTOULL */ |
944 | | #else |
945 | 0 | (void) ull; |
946 | 0 | if (parse_ul(val, &ul) < 0) { |
947 | 0 | return -1; |
948 | 0 | } |
949 | 0 | *uid = ul; |
950 | 0 | #endif /* sizeof(uid_t) != sizeof(long long) */ |
951 | |
|
952 | 0 | return 0; |
953 | 0 | } |
954 | | |
955 | 0 | int pr_str2gid(const char *val, gid_t *gid) { |
956 | 0 | #ifdef HAVE_STRTOULL |
957 | 0 | unsigned long long ull = 0ULL; |
958 | 0 | #endif /* HAVE_STRTOULL */ |
959 | 0 | unsigned long ul = 0UL; |
960 | |
|
961 | 0 | if (val == NULL || |
962 | 0 | gid == NULL) { |
963 | 0 | errno = EINVAL; |
964 | 0 | return -1; |
965 | 0 | } |
966 | | |
967 | | #if SIZEOF_GID_T == SIZEOF_LONG_LONG |
968 | | # ifdef HAVE_STRTOULL |
969 | | if (parse_ull(val, &ull) < 0) { |
970 | | return -1; |
971 | | } |
972 | | *gid = ull; |
973 | | |
974 | | # else |
975 | | if (parse_ul(val, &ul) < 0) { |
976 | | return -1; |
977 | | } |
978 | | *gid = ul; |
979 | | # endif /* HAVE_STRTOULL */ |
980 | | #else |
981 | 0 | (void) ull; |
982 | 0 | if (parse_ul(val, &ul) < 0) { |
983 | 0 | return -1; |
984 | 0 | } |
985 | 0 | *gid = ul; |
986 | 0 | #endif /* sizeof(gid_t) != sizeof(long long) */ |
987 | |
|
988 | 0 | return 0; |
989 | 0 | } |
990 | | |
991 | 0 | const char *pr_uid2str(pool *p, uid_t uid) { |
992 | 0 | static char buf[64]; |
993 | |
|
994 | 0 | memset(&buf, 0, sizeof(buf)); |
995 | 0 | if (uid != (uid_t) -1) { |
996 | | #if SIZEOF_UID_T == SIZEOF_LONG_LONG |
997 | | pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) uid); |
998 | | #else |
999 | 0 | pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) uid); |
1000 | 0 | #endif /* sizeof(uid_t) != sizeof(long long) */ |
1001 | 0 | } else { |
1002 | 0 | pr_snprintf(buf, sizeof(buf)-1, "%d", -1); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | if (p != NULL) { |
1006 | 0 | return pstrdup(p, buf); |
1007 | 0 | } |
1008 | | |
1009 | 0 | return buf; |
1010 | 0 | } |
1011 | | |
1012 | 0 | const char *pr_gid2str(pool *p, gid_t gid) { |
1013 | 0 | static char buf[64]; |
1014 | |
|
1015 | 0 | memset(&buf, 0, sizeof(buf)); |
1016 | 0 | if (gid != (gid_t) -1) { |
1017 | | #if SIZEOF_GID_T == SIZEOF_LONG_LONG |
1018 | | pr_snprintf(buf, sizeof(buf)-1, "%llu", (unsigned long long) gid); |
1019 | | #else |
1020 | 0 | pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) gid); |
1021 | 0 | #endif /* sizeof(gid_t) != sizeof(long long) */ |
1022 | 0 | } else { |
1023 | 0 | pr_snprintf(buf, sizeof(buf)-1, "%d", -1); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | if (p != NULL) { |
1027 | 0 | return pstrdup(p, buf); |
1028 | 0 | } |
1029 | | |
1030 | 0 | return buf; |
1031 | 0 | } |
1032 | | |
1033 | | /* NOTE: Update mod_ban's ban_parse_timestr() to use this function. */ |
1034 | 0 | int pr_str_get_duration(const char *str, int *duration) { |
1035 | 0 | int hours, mins, secs; |
1036 | 0 | int flags = PR_STR_FL_IGNORE_CASE, has_suffix = FALSE; |
1037 | 0 | size_t len; |
1038 | 0 | char *ptr = NULL; |
1039 | |
|
1040 | 0 | if (str == NULL) { |
1041 | 0 | errno = EINVAL; |
1042 | 0 | return -1; |
1043 | 0 | } |
1044 | | |
1045 | 0 | if (sscanf(str, "%2d:%2d:%2d", &hours, &mins, &secs) == 3) { |
1046 | 0 | if (hours < 0 || |
1047 | 0 | mins < 0 || |
1048 | 0 | secs < 0) { |
1049 | 0 | errno = ERANGE; |
1050 | 0 | return -1; |
1051 | 0 | } |
1052 | | |
1053 | 0 | if (duration != NULL) { |
1054 | 0 | *duration = (hours * 60 * 60) + (mins * 60) + secs; |
1055 | 0 | } |
1056 | |
|
1057 | 0 | return 0; |
1058 | 0 | } |
1059 | | |
1060 | 0 | len = strlen(str); |
1061 | 0 | if (len == 0) { |
1062 | 0 | errno = EINVAL; |
1063 | 0 | return -1; |
1064 | 0 | } |
1065 | | |
1066 | | /* Handle the "single component" formats: |
1067 | | * |
1068 | | * If ends with "S", "s", or "sec": parse secs |
1069 | | * If ends with "M", "m", or "min": parse minutes |
1070 | | * If ends with "H", "h", or "hr": parse hours |
1071 | | * |
1072 | | * Otherwise, try to parse as just a number of seconds. |
1073 | | */ |
1074 | | |
1075 | 0 | has_suffix = pr_strnrstr(str, len, "s", 1, flags); |
1076 | 0 | if (has_suffix == FALSE) { |
1077 | 0 | has_suffix = pr_strnrstr(str, len, "sec", 3, flags); |
1078 | 0 | } |
1079 | 0 | if (has_suffix == TRUE) { |
1080 | | /* Parse seconds */ |
1081 | |
|
1082 | 0 | if (sscanf(str, "%d", &secs) == 1) { |
1083 | 0 | if (secs < 0) { |
1084 | 0 | errno = ERANGE; |
1085 | 0 | return -1; |
1086 | 0 | } |
1087 | | |
1088 | 0 | if (duration != NULL) { |
1089 | 0 | *duration = secs; |
1090 | 0 | } |
1091 | |
|
1092 | 0 | return 0; |
1093 | 0 | } |
1094 | | |
1095 | 0 | errno = EINVAL; |
1096 | 0 | return -1; |
1097 | 0 | } |
1098 | | |
1099 | 0 | has_suffix = pr_strnrstr(str, len, "m", 1, flags); |
1100 | 0 | if (has_suffix == FALSE) { |
1101 | 0 | has_suffix = pr_strnrstr(str, len, "min", 3, flags); |
1102 | 0 | } |
1103 | 0 | if (has_suffix == TRUE) { |
1104 | | /* Parse minutes */ |
1105 | |
|
1106 | 0 | if (sscanf(str, "%d", &mins) == 1) { |
1107 | 0 | if (mins < 0) { |
1108 | 0 | errno = ERANGE; |
1109 | 0 | return -1; |
1110 | 0 | } |
1111 | | |
1112 | 0 | if (duration != NULL) { |
1113 | 0 | *duration = (mins * 60); |
1114 | 0 | } |
1115 | |
|
1116 | 0 | return 0; |
1117 | 0 | } |
1118 | | |
1119 | 0 | errno = EINVAL; |
1120 | 0 | return -1; |
1121 | 0 | } |
1122 | | |
1123 | 0 | has_suffix = pr_strnrstr(str, len, "h", 1, flags); |
1124 | 0 | if (has_suffix == FALSE) { |
1125 | 0 | has_suffix = pr_strnrstr(str, len, "hr", 2, flags); |
1126 | 0 | } |
1127 | 0 | if (has_suffix == TRUE) { |
1128 | | /* Parse hours */ |
1129 | |
|
1130 | 0 | if (sscanf(str, "%d", &hours) == 1) { |
1131 | 0 | if (hours < 0) { |
1132 | 0 | errno = ERANGE; |
1133 | 0 | return -1; |
1134 | 0 | } |
1135 | | |
1136 | 0 | if (duration != NULL) { |
1137 | 0 | *duration = (hours * 60 * 60); |
1138 | 0 | } |
1139 | |
|
1140 | 0 | return 0; |
1141 | 0 | } |
1142 | | |
1143 | 0 | errno = EINVAL; |
1144 | 0 | return -1; |
1145 | 0 | } |
1146 | | |
1147 | | /* Use strtol(3) here, check for trailing garbage, etc. */ |
1148 | 0 | secs = (int) strtol(str, &ptr, 10); |
1149 | 0 | if (ptr && *ptr) { |
1150 | | /* Not a bare number, but a string with non-numeric characters. */ |
1151 | 0 | errno = EINVAL; |
1152 | 0 | return -1; |
1153 | 0 | } |
1154 | | |
1155 | 0 | if (secs < 0) { |
1156 | 0 | errno = ERANGE; |
1157 | 0 | return -1; |
1158 | 0 | } |
1159 | | |
1160 | 0 | if (duration != NULL) { |
1161 | 0 | *duration = secs; |
1162 | 0 | } |
1163 | |
|
1164 | 0 | return 0; |
1165 | 0 | } |
1166 | | |
1167 | 0 | int pr_str_get_nbytes(const char *str, const char *units, off_t *nbytes) { |
1168 | 0 | off_t sz; |
1169 | 0 | char *ptr = NULL; |
1170 | 0 | float factor = 0.0; |
1171 | |
|
1172 | 0 | if (str == NULL) { |
1173 | 0 | errno = EINVAL; |
1174 | 0 | return -1; |
1175 | 0 | } |
1176 | | |
1177 | | /* No negative numbers. */ |
1178 | 0 | if (*str == '-') { |
1179 | 0 | errno = EINVAL; |
1180 | 0 | return -1; |
1181 | 0 | } |
1182 | | |
1183 | 0 | if (units == NULL || |
1184 | 0 | *units == '\0') { |
1185 | 0 | factor = 1.0; |
1186 | |
|
1187 | 0 | } else if (strncasecmp(units, "KB", 3) == 0) { |
1188 | 0 | factor = 1024.0; |
1189 | |
|
1190 | 0 | } else if (strncasecmp(units, "MB", 3) == 0) { |
1191 | 0 | factor = 1024.0 * 1024.0; |
1192 | |
|
1193 | 0 | } else if (strncasecmp(units, "GB", 3) == 0) { |
1194 | 0 | factor = 1024.0 * 1024.0 * 1024.0; |
1195 | |
|
1196 | 0 | } else if (strncasecmp(units, "TB", 3) == 0) { |
1197 | 0 | factor = 1024.0 * 1024.0 * 1024.0 * 1024.0; |
1198 | |
|
1199 | 0 | } else if (strncasecmp(units, "B", 2) == 0) { |
1200 | 0 | factor = 1.0; |
1201 | |
|
1202 | 0 | } else { |
1203 | 0 | errno = EINVAL; |
1204 | 0 | return -1; |
1205 | 0 | } |
1206 | | |
1207 | 0 | errno = 0; |
1208 | |
|
1209 | 0 | #ifdef HAVE_STRTOULL |
1210 | 0 | sz = strtoull(str, &ptr, 10); |
1211 | | #else |
1212 | | sz = strtoul(str, &ptr, 10); |
1213 | | #endif /* !HAVE_STRTOULL */ |
1214 | |
|
1215 | 0 | if (errno == ERANGE) { |
1216 | 0 | return -1; |
1217 | 0 | } |
1218 | | |
1219 | 0 | if (ptr != NULL && *ptr) { |
1220 | | /* Error parsing the given string */ |
1221 | 0 | errno = EINVAL; |
1222 | 0 | return -1; |
1223 | 0 | } |
1224 | | |
1225 | | /* Don't bother applying the factor if the result will overflow the result. */ |
1226 | 0 | #ifdef ULLONG_MAX |
1227 | 0 | if (sz > (ULLONG_MAX / factor)) { |
1228 | | #else |
1229 | | if (sz > (ULONG_MAX / factor)) { |
1230 | | #endif /* !ULLONG_MAX */ |
1231 | 0 | errno = ERANGE; |
1232 | 0 | return -1; |
1233 | 0 | } |
1234 | | |
1235 | 0 | if (nbytes != NULL) { |
1236 | 0 | *nbytes = (off_t) (sz * factor); |
1237 | 0 | } |
1238 | |
|
1239 | 0 | return 0; |
1240 | 0 | } |
1241 | | |
1242 | 0 | char *pr_str_get_word(char **cp, int flags) { |
1243 | 0 | char *res, *dst; |
1244 | 0 | int quote_mode = FALSE; |
1245 | |
|
1246 | 0 | if (cp == NULL || |
1247 | 0 | !*cp || |
1248 | 0 | !**cp) { |
1249 | 0 | errno = EINVAL; |
1250 | 0 | return NULL; |
1251 | 0 | } |
1252 | | |
1253 | 0 | if (!(flags & PR_STR_FL_PRESERVE_WHITESPACE)) { |
1254 | 0 | while (**cp && PR_ISSPACE(**cp)) { |
1255 | 0 | pr_signals_handle(); |
1256 | 0 | (*cp)++; |
1257 | 0 | } |
1258 | 0 | } |
1259 | |
|
1260 | 0 | if (!**cp) { |
1261 | 0 | return NULL; |
1262 | 0 | } |
1263 | | |
1264 | 0 | res = dst = *cp; |
1265 | |
|
1266 | 0 | if (!(flags & PR_STR_FL_PRESERVE_COMMENTS)) { |
1267 | | /* Stop processing at start of an inline comment. */ |
1268 | 0 | if (**cp == '#') { |
1269 | 0 | return NULL; |
1270 | 0 | } |
1271 | 0 | } |
1272 | | |
1273 | 0 | if (!(flags & PR_STR_FL_IGNORE_QUOTES)) { |
1274 | 0 | if (**cp == '\"') { |
1275 | 0 | quote_mode = TRUE; |
1276 | 0 | (*cp)++; |
1277 | 0 | } |
1278 | 0 | } |
1279 | |
|
1280 | 0 | while (**cp && (quote_mode ? (**cp != '\"') : !PR_ISSPACE(**cp))) { |
1281 | 0 | pr_signals_handle(); |
1282 | |
|
1283 | 0 | if (**cp == '\\' && |
1284 | 0 | quote_mode == TRUE) { |
1285 | | /* Escaped char */ |
1286 | 0 | if (*((*cp)+1)) { |
1287 | 0 | *dst++ = *(++(*cp)); |
1288 | 0 | (*cp)++; |
1289 | 0 | continue; |
1290 | 0 | } |
1291 | 0 | } |
1292 | | |
1293 | 0 | *dst++ = **cp; |
1294 | 0 | (*cp)++; |
1295 | 0 | } |
1296 | |
|
1297 | 0 | if (**cp) { |
1298 | 0 | (*cp)++; |
1299 | 0 | } |
1300 | |
|
1301 | 0 | *dst = '\0'; |
1302 | 0 | return res; |
1303 | 0 | } |
1304 | | |
1305 | | /* get_token tokenizes a string, increments the src pointer to the next |
1306 | | * non-separator in the string. If the src string is empty or NULL, the next |
1307 | | * token returned is NULL. |
1308 | | */ |
1309 | 0 | char *pr_str_get_token2(char **src, char *sep, size_t *token_len) { |
1310 | 0 | char *token; |
1311 | 0 | size_t len = 0; |
1312 | |
|
1313 | 0 | if (src == NULL || |
1314 | 0 | *src == NULL || |
1315 | 0 | **src == '\0' || |
1316 | 0 | sep == NULL) { |
1317 | |
|
1318 | 0 | if (token_len != NULL) { |
1319 | 0 | *token_len = len; |
1320 | 0 | } |
1321 | |
|
1322 | 0 | errno = EINVAL; |
1323 | 0 | return NULL; |
1324 | 0 | } |
1325 | | |
1326 | 0 | token = *src; |
1327 | |
|
1328 | 0 | while (**src && !strchr(sep, **src)) { |
1329 | 0 | (*src)++; |
1330 | 0 | len++; |
1331 | 0 | } |
1332 | |
|
1333 | 0 | if (**src) { |
1334 | 0 | *(*src)++ = '\0'; |
1335 | 0 | } |
1336 | |
|
1337 | 0 | if (token_len != NULL) { |
1338 | 0 | *token_len = len; |
1339 | 0 | } |
1340 | |
|
1341 | 0 | return token; |
1342 | 0 | } |
1343 | | |
1344 | 0 | char *pr_str_get_token(char **src, char *sep) { |
1345 | 0 | return pr_str_get_token2(src, sep, NULL); |
1346 | 0 | } |
1347 | | |
1348 | 0 | int pr_str_is_boolean(const char *str) { |
1349 | 0 | if (str == NULL) { |
1350 | 0 | errno = EINVAL; |
1351 | 0 | return -1; |
1352 | 0 | } |
1353 | | |
1354 | 0 | if (strncasecmp(str, "on", 3) == 0) { |
1355 | 0 | return TRUE; |
1356 | 0 | } |
1357 | | |
1358 | 0 | if (strncasecmp(str, "off", 4) == 0) { |
1359 | 0 | return FALSE; |
1360 | 0 | } |
1361 | | |
1362 | 0 | if (strncasecmp(str, "yes", 4) == 0) { |
1363 | 0 | return TRUE; |
1364 | 0 | } |
1365 | | |
1366 | 0 | if (strncasecmp(str, "no", 3) == 0) { |
1367 | 0 | return FALSE; |
1368 | 0 | } |
1369 | | |
1370 | 0 | if (strncasecmp(str, "true", 5) == 0) { |
1371 | 0 | return TRUE; |
1372 | 0 | } |
1373 | | |
1374 | 0 | if (strncasecmp(str, "false", 6) == 0) { |
1375 | 0 | return FALSE; |
1376 | 0 | } |
1377 | | |
1378 | 0 | if (strncasecmp(str, "1", 2) == 0) { |
1379 | 0 | return TRUE; |
1380 | 0 | } |
1381 | | |
1382 | 0 | if (strncasecmp(str, "0", 2) == 0) { |
1383 | 0 | return FALSE; |
1384 | 0 | } |
1385 | | |
1386 | 0 | errno = EINVAL; |
1387 | 0 | return -1; |
1388 | 0 | } |
1389 | | |
1390 | | /* Return true if str contains any of the glob(7) characters. */ |
1391 | 0 | int pr_str_is_fnmatch(const char *str) { |
1392 | 0 | int have_bracket = 0; |
1393 | |
|
1394 | 0 | if (str == NULL) { |
1395 | 0 | return FALSE; |
1396 | 0 | } |
1397 | | |
1398 | 0 | while (*str) { |
1399 | 0 | switch (*str) { |
1400 | 0 | case '?': |
1401 | 0 | case '*': |
1402 | 0 | return TRUE; |
1403 | | |
1404 | 0 | case '\\': |
1405 | | /* If the next character is NUL, we've reached the end of the string. */ |
1406 | 0 | if (*(str+1) == '\0') { |
1407 | 0 | return FALSE; |
1408 | 0 | } |
1409 | | |
1410 | | /* Skip past the escaped character, i.e. the next character. */ |
1411 | 0 | str++; |
1412 | 0 | break; |
1413 | | |
1414 | 0 | case '[': |
1415 | 0 | have_bracket++; |
1416 | 0 | break; |
1417 | | |
1418 | 0 | case ']': |
1419 | 0 | if (have_bracket) { |
1420 | 0 | return TRUE; |
1421 | 0 | } |
1422 | 0 | break; |
1423 | | |
1424 | 0 | default: |
1425 | 0 | break; |
1426 | 0 | } |
1427 | | |
1428 | 0 | str++; |
1429 | 0 | } |
1430 | | |
1431 | 0 | return FALSE; |
1432 | 0 | } |