]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | /* | |
4 | * Concatenates/copies strings. In any case, terminates in all cases | |
5 | * with '\0' and moves the @dest pointer forward to the added '\0'. | |
6 | * Returns the remaining size, and 0 if the string was truncated. | |
7 | * | |
8 | * Due to the intended usage, these helpers silently noop invocations | |
9 | * having zero size. This is technically an exception to the above | |
10 | * statement "terminates in all cases". It's unexpected for such calls to | |
11 | * occur outside of a loop where this is the preferred behavior. | |
12 | */ | |
13 | ||
14 | #include <stdio.h> | |
15 | #include <string.h> | |
16 | ||
17 | #include "strxcpyx.h" | |
18 | ||
19 | size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) { | |
20 | bool truncated = false; | |
21 | ||
22 | assert(dest); | |
23 | assert(src); | |
24 | ||
25 | if (size == 0) { | |
26 | if (ret_truncated) | |
27 | *ret_truncated = len > 0; | |
28 | return 0; | |
29 | } | |
30 | ||
31 | if (len >= size) { | |
32 | if (size > 1) | |
33 | *dest = mempcpy(*dest, src, size-1); | |
34 | size = 0; | |
35 | truncated = true; | |
36 | } else if (len > 0) { | |
37 | *dest = mempcpy(*dest, src, len); | |
38 | size -= len; | |
39 | } | |
40 | ||
41 | if (ret_truncated) | |
42 | *ret_truncated = truncated; | |
43 | ||
44 | *dest[0] = '\0'; | |
45 | return size; | |
46 | } | |
47 | ||
48 | size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) { | |
49 | assert(dest); | |
50 | assert(src); | |
51 | ||
52 | return strnpcpy_full(dest, size, src, strlen(src), ret_truncated); | |
53 | } | |
54 | ||
55 | size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { | |
56 | bool truncated = false; | |
57 | va_list va; | |
58 | int i; | |
59 | ||
60 | assert(dest); | |
61 | assert(src); | |
62 | ||
63 | va_start(va, src); | |
64 | i = vsnprintf(*dest, size, src, va); | |
65 | va_end(va); | |
66 | ||
67 | if (i < (int) size) { | |
68 | *dest += i; | |
69 | size -= i; | |
70 | } else { | |
71 | size = 0; | |
72 | truncated = i > 0; | |
73 | } | |
74 | ||
75 | if (ret_truncated) | |
76 | *ret_truncated = truncated; | |
77 | ||
78 | return size; | |
79 | } | |
80 | ||
81 | size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { | |
82 | bool truncated = false; | |
83 | va_list va; | |
84 | ||
85 | assert(dest); | |
86 | assert(src); | |
87 | ||
88 | va_start(va, src); | |
89 | do { | |
90 | bool t; | |
91 | ||
92 | size = strpcpy_full(dest, size, src, &t); | |
93 | truncated = truncated || t; | |
94 | src = va_arg(va, char *); | |
95 | } while (src); | |
96 | va_end(va); | |
97 | ||
98 | if (ret_truncated) | |
99 | *ret_truncated = truncated; | |
100 | return size; | |
101 | } | |
102 | ||
103 | size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) { | |
104 | char *s; | |
105 | ||
106 | assert(dest); | |
107 | assert(src); | |
108 | ||
109 | s = dest; | |
110 | return strnpcpy_full(&s, size, src, len, ret_truncated); | |
111 | } | |
112 | ||
113 | size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) { | |
114 | assert(dest); | |
115 | assert(src); | |
116 | ||
117 | return strnscpy_full(dest, size, src, strlen(src), ret_truncated); | |
118 | } | |
119 | ||
120 | size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) { | |
121 | bool truncated = false; | |
122 | va_list va; | |
123 | char *s; | |
124 | ||
125 | assert(dest); | |
126 | assert(src); | |
127 | ||
128 | va_start(va, src); | |
129 | s = dest; | |
130 | do { | |
131 | bool t; | |
132 | ||
133 | size = strpcpy_full(&s, size, src, &t); | |
134 | truncated = truncated || t; | |
135 | src = va_arg(va, char *); | |
136 | } while (src); | |
137 | va_end(va); | |
138 | ||
139 | if (ret_truncated) | |
140 | *ret_truncated = truncated; | |
141 | ||
142 | return size; | |
143 | } |