├── Makefile ├── README ├── comparison.c ├── example.c ├── libste.3 ├── ste.h ├── steccpy.c ├── stechr.c ├── stecpe.c ├── stecpy-builtin.c ├── stecpy-memccpy.c ├── stecpy.c ├── steprn.c └── tests.c /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g -O2 -Wall -Wno-switch -Wextra -Wwrite-strings 2 | 3 | all: libste.a example 4 | 5 | libste.a: stechr.o stecpe.o stecpy.o steccpy.o steprn.o 6 | $(AR) $(ARFLAGS) $@ $^ 7 | 8 | example: example.o libste.a 9 | 10 | tests: tests.o libste.a 11 | 12 | check: tests FRC 13 | prove -v ./tests 14 | 15 | README: libste.3 16 | mandoc -Tutf8 $^ | col -bx >$@ 17 | 18 | 19 | clean: FRC 20 | rm -f *.o *.a example tests 21 | 22 | FRC: 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | LIBSTE(3) Library Functions Manual LIBSTE(3) 2 | 3 | NAME 4 | stecpy, stecpe, stechr, steprn – string library based on string ends 5 | 6 | SYNOPSIS 7 | #include 8 | 9 | char * 10 | stecpy(char *dst, char *end, const char *src); 11 | 12 | char * 13 | steccpy(char *dst, char *end, const char *src, int c); 14 | 15 | char * 16 | stecpe(char *dst, char *end, const char *src, const char *srcend); 17 | 18 | char * 19 | stechr(const char *src, const char *end, int c); 20 | 21 | char * 22 | steprn(char *dst, char *end, const char *fmt, ...); 23 | 24 | DESCRIPTION 25 | libste provides five useful functions for dealing with strings. 26 | 27 | stecpy copies the NUL-terminated string src to dst, but writes no 28 | characters beyond end. If any characters are copied, dst will be NUL- 29 | terminated and the return value is a pointer to the NUL byte. On 30 | truncation, end is returned. 31 | 32 | steccpy copies the NUL-terminated string src to dst, stopping when the 33 | character c is found. It writes no characters beyond end. If any 34 | characters are copied, dst will be NUL-terminated and the return value is 35 | a pointer to the NUL byte. On truncation, end is returned. 36 | 37 | stecpe copies the string between src and srcend to dst, but writes no 38 | characters beyond end. If any characters are copied, dst will be NUL- 39 | terminated and the return value is a pointer to the NUL byte. On 40 | truncation, end is returned. 41 | 42 | stechr returns a pointer to the first occurence of c (converted to a 43 | char) in the NUL-terminated string pointed to by src, but reads no 44 | characters beyond end. If c is not found, stechr returns a pointer to 45 | the first NUL byte in src, or end if none was found. 46 | 47 | steprn uses vsnprintf(3) to write formatted output to dst, but writes no 48 | characters beyond end. If any characters are written, dst will be NUL- 49 | terminated and the return value is a pointer to the NUL byte. On 50 | truncation, end is returned. 51 | 52 | Note that it is safe to pass the return value of all functions listed 53 | above as argument for dst when the same end is reused. In this case, the 54 | function call does nothing but return dst again. At any point, 55 | truncation can be checked by comparing the return value to end. 56 | 57 | IMPLEMENTATION DETAILS 58 | libste is written in portable C99. 59 | 60 | Functions do not use vectorization to keep code size small and because 61 | the inputs are expected to be small enough to not benefit from it. 62 | 63 | The functions can be vendored or inlined into your codebase easily. 64 | 65 | AUTHORS 66 | Leah Neukirchen 67 | 68 | LICENSE 69 | libste is in the public domain. 70 | 71 | To the extent possible under law, the creator of this work has waived all 72 | copyright and related or neighboring rights to this work. 73 | 74 | http://creativecommons.org/publicdomain/zero/1.0/ 75 | 76 | Void Linux November 5, 2021 Void Linux 77 | -------------------------------------------------------------------------------- /comparison.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef __GLIBC__ 5 | #include 6 | #endif 7 | 8 | #include "ste.h" 9 | 10 | /* Task: write a function that appends three char* arguments into 11 | a 1024 byte fixed buffer and return a pointer to it. 12 | Return NULL if the strings don't fit. */ 13 | 14 | /* Annotations of suboptimal behavior: 15 | (T) traverses the string to find the end 16 | (I) doesn't support incremental string construction 17 | (L) overhead strlen 18 | (Z) buffer is not NUL-terminated at time of truncation detection 19 | */ 20 | 21 | char * 22 | path3_stecpy(char *a, char *b, char *c) 23 | { 24 | static char buf[1024]; 25 | char *end = buf + sizeof buf; 26 | 27 | char *pos = buf; 28 | pos = stecpy(pos, end, a); 29 | pos = stecpy(pos, end, b); 30 | pos = stecpy(pos, end, c); 31 | 32 | if (pos == end) 33 | return 0; 34 | return buf; 35 | } 36 | 37 | char *str_ecpy(char *to, char *e, char *from); 38 | 39 | char * 40 | path3_strecpy(char *a, char *b, char *c) 41 | { 42 | static char buf[1024]; 43 | char *end = buf + sizeof buf; 44 | 45 | char *pos = buf; 46 | pos = str_ecpy(pos, end, a); 47 | pos = str_ecpy(pos, end, b); 48 | pos = str_ecpy(pos, end, c); 49 | 50 | if (pos == end - 1) // almost the same as stecpy but easier to get wrong 51 | return 0; 52 | return buf; 53 | } 54 | 55 | char * 56 | path3_strncat(char *a, char *b, char *c) 57 | { 58 | static char buf[1024]; 59 | size_t n = sizeof buf - 1; 60 | 61 | buf[0] = 0; 62 | if (strlen(a) > n) // (L) 63 | return 0; 64 | strncat(buf, a, n); 65 | n -= strlen(a); // (L) 66 | if (strlen(b) > n) // (L) 67 | return 0; 68 | strncat(buf, b, n); // (T) 69 | n -= strlen(b); // (L) 70 | if (strlen(c) > n) // (L) 71 | return 0; 72 | strncat(buf, c, n); // (T) 73 | 74 | return buf; 75 | } 76 | 77 | char * 78 | path3_strlcat(char *a, char *b, char *c) 79 | { 80 | static char buf[1024]; 81 | 82 | strlcpy(buf, a, sizeof buf); 83 | strlcat(buf, b, sizeof buf); // (T) 84 | if (strlcat(buf, c, sizeof buf) >= sizeof buf) // (T) 85 | return 0; 86 | 87 | return buf; 88 | } 89 | 90 | char * 91 | path3_snprintf(char *a, char *b, char *c) 92 | { 93 | static char buf[1024]; 94 | 95 | if (snprintf(buf, sizeof buf, "%s%s%s", a, b, c) >= sizeof buf) // (I) 96 | return 0; 97 | 98 | return buf; 99 | } 100 | 101 | ssize_t str_scpy(char *dst, const char *src, size_t count); 102 | 103 | char * 104 | path3_strscpy(char *a, char *b, char *c) 105 | { 106 | static char buf[1024]; 107 | 108 | ssize_t r; 109 | size_t l = 0; 110 | r = str_scpy(buf + l, a, sizeof buf - l); 111 | if (r < 0) 112 | return 0; 113 | l += r; 114 | r = str_scpy(buf + l, b, sizeof buf - l); 115 | if (r < 0) 116 | return 0; 117 | l += r; 118 | r = str_scpy(buf + l, c, sizeof buf - l); 119 | if (r < 0) 120 | return 0; 121 | 122 | return buf; 123 | } 124 | 125 | char * 126 | path3_memcpy(char *a, char *b, char *c) 127 | { 128 | static char buf[1024]; 129 | size_t s = 0; 130 | 131 | if (strlen(a) >= sizeof buf - s) // (L) 132 | return 0; 133 | memcpy(buf + s, a, strlen(a)); 134 | s += strlen(a); 135 | 136 | if (strlen(b) >= sizeof buf - s) // (L) 137 | return 0; 138 | memcpy(buf + s, b, strlen(b)); 139 | s += strlen(b); 140 | 141 | if (strlen(c) >= sizeof buf - s) // (L) 142 | return 0; 143 | memcpy(buf + s, c, strlen(c)); 144 | 145 | return buf; 146 | } 147 | 148 | size_t str_copyb(char *s, const char *t, size_t max); 149 | 150 | char * 151 | path3_str_copyb(char *a, char *b, char *c) 152 | { 153 | static char buf[1024]; 154 | 155 | size_t n = 0; 156 | 157 | n += str_copyb(buf + n, a, sizeof buf - n); 158 | if (n == sizeof buf) 159 | return 0; // (Z) 160 | n += str_copyb(buf + n, b, sizeof buf - n); 161 | if (n == sizeof buf) 162 | return 0; // (Z) 163 | n += str_copyb(buf + n, c, sizeof buf - n); 164 | if (n == sizeof buf) 165 | return 0; // (Z) 166 | 167 | return buf; 168 | } 169 | 170 | int 171 | main() 172 | { 173 | printf("%s\n", path3_stecpy("abc", "def", "ghi")); 174 | printf("%s\n", path3_strecpy("abc", "def", "ghi")); 175 | printf("%s\n", path3_strncat("abc", "def", "ghi")); 176 | printf("%s\n", path3_strlcat("abc", "def", "ghi")); 177 | printf("%s\n", path3_snprintf("abc", "def", "ghi")); 178 | printf("%s\n", path3_strscpy("abc", "def", "ghi")); 179 | printf("%s\n", path3_memcpy("abc", "def", "ghi")); 180 | printf("%s\n", path3_str_copyb("abc", "def", "ghi")); 181 | } 182 | 183 | 184 | 185 | /* Plan 9 strecpy(3) */ 186 | char* 187 | str_ecpy(char *to, char *e, char *from) 188 | { 189 | if (to >= e) 190 | return to; 191 | 192 | to = memccpy(to, from, '\0', e - to); 193 | if (!to) { 194 | to = e - 1; 195 | *to = '\0'; 196 | } else { 197 | to--; 198 | } 199 | 200 | return to; 201 | } 202 | 203 | /* Linux kernel strscpy */ 204 | ssize_t 205 | str_scpy(char *dst, const char *src, size_t count) 206 | { 207 | if (count == 0) 208 | return -E2BIG; // no space for trailing null 209 | 210 | char *end = memccpy(dst, src, 0, count); 211 | if (!end) { 212 | dst[count] = 0; 213 | return -E2BIG; 214 | } 215 | 216 | return end - dst - 1; 217 | } 218 | 219 | /* djb-style, but with proper integer widths */ 220 | size_t 221 | str_copyb(char *s, const char *t, size_t max) 222 | { 223 | size_t len = 0; 224 | 225 | while (max-- > 0) { 226 | if (!(*s = *t)) { return len; } ++s; ++t; ++len; 227 | } 228 | 229 | return len; 230 | } 231 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ste.h" 7 | 8 | /* example of libste usage: iterate over $PATH and append argv[1], 9 | print the entries that fit into PATH_MAX. */ 10 | 11 | int 12 | main(int argc, char *argv[]) 13 | { 14 | const char *path = getenv("PATH"); 15 | if (!path) 16 | path = ""; 17 | 18 | const char *program = argc > 1 ? argv[1] : "xyzzy"; 19 | 20 | const char *pathend = path + strlen(path); 21 | 22 | char buf[PATH_MAX]; 23 | char *bufend = buf + sizeof buf; 24 | 25 | while (1) { 26 | char *pos = buf; 27 | 28 | char *colon = stechr(path, pathend, ':'); 29 | if (path == colon) /* empty entry */ 30 | pos = stecpy(buf, bufend, "."); 31 | else 32 | pos = stecpe(buf, bufend, path, colon); 33 | 34 | pos = steprn(pos, bufend, "/%s", program); 35 | 36 | if (pos < bufend) { /* no trunaction */ 37 | printf("%s\n", buf); 38 | } 39 | 40 | if (colon == pathend) 41 | break; 42 | path = colon + 1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /libste.3: -------------------------------------------------------------------------------- 1 | .Dd November 5, 2021 2 | .Dt LIBSTE 3 3 | .Os 4 | .Sh NAME 5 | .Nm stecpy , 6 | .Nm stecpe , 7 | .Nm stechr , 8 | .Nm steprn 9 | .Nd string library based on string ends 10 | .Sh SYNOPSIS 11 | .In ste.h 12 | .Ft "char *" 13 | .Fn stecpy "char *dst" "char *end" "const char *src" 14 | .Ft "char *" 15 | .Fn steccpy "char *dst" "char *end" "const char *src" "int c" 16 | .Ft "char *" 17 | .Fn stecpe "char *dst" "char *end" "const char *src" "const char *srcend" 18 | .Ft "char *" 19 | .Fn stechr "const char *src" "const char *end" "int c" 20 | .Ft "char *" 21 | .Fn steprn "char *dst" "char *end" "const char *fmt" "..." 22 | .Sh DESCRIPTION 23 | .Nm libste 24 | provides five useful functions for dealing with strings. 25 | .Pp 26 | .Nm stecpy 27 | copies the NUL-terminated string 28 | .Fa src 29 | to 30 | .Fa dst , 31 | but writes no characters beyond 32 | .Fa end . 33 | If any characters are copied, 34 | .Fa dst 35 | will be NUL-terminated 36 | and the return value is a pointer to the NUL byte. 37 | On truncation, 38 | .Fa end 39 | is returned. 40 | .Pp 41 | .Nm steccpy 42 | copies the NUL-terminated string 43 | .Fa src 44 | to 45 | .Fa dst , 46 | stopping when the character 47 | .Fa c 48 | is found. 49 | It writes no characters beyond 50 | .Fa end . 51 | If any characters are copied, 52 | .Fa dst 53 | will be NUL-terminated 54 | and the return value is a pointer to the NUL byte. 55 | On truncation, 56 | .Fa end 57 | is returned. 58 | .Pp 59 | .Nm stecpe 60 | copies the string between 61 | .Fa src 62 | and 63 | .Fa srcend 64 | to 65 | .Fa dst , 66 | but writes no characters beyond 67 | .Fa end . 68 | If any characters are copied, 69 | .Fa dst 70 | will be NUL-terminated 71 | and the return value is a pointer to the NUL byte. 72 | On truncation, 73 | .Fa end 74 | is returned. 75 | .Pp 76 | .Nm stechr 77 | returns a pointer to the first occurence of 78 | .Fa c 79 | .Pq converted to a Vt char 80 | in the NUL-terminated string pointed to by 81 | .Fa src , 82 | but reads no characters beyond 83 | .Fa end . 84 | If 85 | .Fa c 86 | is not found, 87 | .Nm stechr 88 | returns a pointer to the first NUL byte in 89 | .Fa src , 90 | or 91 | .Fa end 92 | if none was found. 93 | .Pp 94 | .Nm steprn 95 | uses 96 | .Xr vsnprintf 3 97 | to write formatted output to 98 | .Fa dst , 99 | but writes no characters beyond 100 | .Fa end . 101 | If any characters are written, 102 | .Fa dst 103 | will be NUL-terminated 104 | and the return value is a pointer to the NUL byte. 105 | On truncation, 106 | .Fa end 107 | is returned. 108 | .Pp 109 | Note that it is safe to pass the return value of all functions listed above 110 | as argument for 111 | .Fa dst 112 | when the same 113 | .Fa end 114 | is reused. 115 | In this case, the function call does nothing but return 116 | .Fa dst 117 | again. 118 | At any point, truncation can be checked by comparing the return value to 119 | .Fa end . 120 | .\" .Sh RETURN VALUES 121 | .\" .Sh SEE ALSO 122 | .\" .Sh STANDARDS 123 | .\" .Sh HISTORY 124 | .Sh IMPLEMENTATION DETAILS 125 | .Nm libste 126 | is written in portable C99. 127 | .Pp 128 | Functions do not use vectorization to keep code size small 129 | and because the inputs are expected to be small enough to not benefit from it. 130 | .Pp 131 | The functions can be vendored or inlined into your codebase easily. 132 | .Sh AUTHORS 133 | .An Leah Neukirchen Aq Mt leah@vuxu.org 134 | .Sh LICENSE 135 | .Nm libste 136 | is in the public domain. 137 | .Pp 138 | To the extent possible under law, 139 | the creator of this work 140 | has waived all copyright and related or 141 | neighboring rights to this work. 142 | .Pp 143 | .Lk http://creativecommons.org/publicdomain/zero/1.0/ 144 | -------------------------------------------------------------------------------- /ste.h: -------------------------------------------------------------------------------- 1 | char *stecpy(char *dst, char *end, const char *src); 2 | char *steccpy(char *dst, char *end, const char *src, int c); 3 | char *stecpe(char *dst, char *end, const char *src, const char *srcend); 4 | char *stechr(const char *src, const char *end, int c); 5 | char *steprn(char *dst, char *end, const char *fmt, ...); 6 | -------------------------------------------------------------------------------- /steccpy.c: -------------------------------------------------------------------------------- 1 | char * 2 | steccpy(char *dst, char *end, const char *src, int c) 3 | { 4 | if (dst >= end) 5 | return dst; 6 | 7 | while (dst < end && *src != c && (*dst = *src)) 8 | src++, dst++; 9 | 10 | if (dst == end) 11 | dst[-1] = 0; 12 | else if (*src == c) 13 | dst[0] = 0; 14 | 15 | return dst; 16 | } 17 | -------------------------------------------------------------------------------- /stechr.c: -------------------------------------------------------------------------------- 1 | char * 2 | stechr(const char *src, const char *end, int c) 3 | { 4 | while (src < end && *src && *(unsigned char *)src != (unsigned char)c) 5 | src++; 6 | 7 | return (char *)src; 8 | } 9 | -------------------------------------------------------------------------------- /stecpe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char * 5 | stecpe(char *dst, const char *end, const char *src, const char *srcend) 6 | { 7 | if (dst >= end) 8 | return dst; 9 | 10 | ptrdiff_t l = end - dst - 1; 11 | size_t t = 1; 12 | if (srcend - src < l) { 13 | l = srcend - src; 14 | t = 0; 15 | } 16 | 17 | memcpy(dst, src, l); 18 | dst[l] = 0; 19 | 20 | return dst + l + t; 21 | } 22 | -------------------------------------------------------------------------------- /stecpy-builtin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char * 4 | stecpy(char *dst, char *end, const char *src) 5 | { 6 | if (dst >= end) 7 | return dst; 8 | 9 | size_t n = strnlen(src, end - dst); 10 | 11 | if (n == end - dst) { 12 | memcpy(dst, src, n - 1); 13 | end[-1] = 0; 14 | return end; 15 | } 16 | 17 | return memcpy(dst, src, n + 1) + n; 18 | } 19 | -------------------------------------------------------------------------------- /stecpy-memccpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char * 4 | stecpy(char *dst, char *end, const char *src) 5 | { 6 | if (dst >= end) 7 | return dst; 8 | 9 | dst = memccpy(dst, src, '\0', end - dst); 10 | if (!dst) { 11 | end[-1] = 0; 12 | return end; 13 | } 14 | 15 | return dst - 1; 16 | } 17 | -------------------------------------------------------------------------------- /stecpy.c: -------------------------------------------------------------------------------- 1 | char * 2 | stecpy(char *dst, char *end, const char *src) 3 | { 4 | if (dst >= end) 5 | return dst; 6 | 7 | while (dst < end && (*dst = *src)) 8 | src++, dst++; 9 | 10 | if (dst == end) 11 | dst[-1] = 0; 12 | 13 | return dst; 14 | } 15 | -------------------------------------------------------------------------------- /steprn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char * 5 | steprn(char *dst, char *end, const char *fmt, ...) 6 | { 7 | if (dst >= end) 8 | return end; 9 | 10 | va_list ap; 11 | va_start(ap, fmt); 12 | int r = vsnprintf(dst, end - dst, fmt, ap); 13 | va_end(ap); 14 | 15 | if (r < 0) { 16 | /* snprintf only fails for silly reasons: 17 | truncate what was written, behave as noop. */ 18 | *dst = 0; 19 | return dst; 20 | } 21 | 22 | return r > end - dst ? end : dst + r; 23 | } 24 | -------------------------------------------------------------------------------- /tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ste.h" 5 | 6 | static int status; 7 | 8 | void 9 | is(const char *desc, int ok) 10 | { 11 | if (!ok) 12 | status = 1; 13 | printf("%s - %s\n", ok ? "ok" : "not ok", desc); 14 | } 15 | 16 | int 17 | main() 18 | { 19 | printf("1..49\n"); 20 | 21 | printf("# stecpy\n"); 22 | 23 | char buf[16]; 24 | char *end = buf + sizeof buf; 25 | char buf2[32] = "stringxyzxyzxyzxyzxyz"; 26 | char *pos, *prevpos; 27 | 28 | pos = buf; 29 | pos = stecpy(pos, end, "abc"); 30 | is("1x3 = 3", strlen(buf) == 3); 31 | pos = stecpy(pos, end, "def"); 32 | is("2x3 = 6", strlen(buf) == 6); 33 | pos = stecpy(pos, end, "ghi"); 34 | is("3x3 = 9", strlen(buf) == 9); 35 | pos = stecpy(pos, end, "jkl"); 36 | is("4x3 = 12", strlen(buf) == 12); 37 | pos = stecpy(pos, end, "mno"); 38 | is("5x3 = 15", strlen(buf) == 15); 39 | pos = stecpy(pos, end, "full"); 40 | is("buffer is full", strlen(buf) == 15); 41 | is("return value is end", pos == end); 42 | pos = stecpy(pos, end, "fuller"); 43 | is("buffer doesn't get fuller", strlen(buf) == 15); 44 | is("return value is end", pos == end); 45 | 46 | pos = buf; 47 | pos = stecpy(pos, end, "abcdefghijklmnopq"); 48 | is("truncation", strlen(buf) == 15); 49 | is("return value is end", pos == end); 50 | 51 | pos = buf; 52 | pos = stecpy(pos, end, "xyz"); 53 | pos = stecpy(prevpos=pos, end, ""); 54 | is("empty append works", strlen(buf) == 3); 55 | is("return value is unchanged", pos == prevpos); 56 | 57 | pos = buf; 58 | pos = stecpy(pos, end, "xyz"); 59 | pos = stecpy(pos, pos, "foo"); 60 | is("final append works", strlen(buf) == 3); 61 | 62 | printf("# steprn\n"); 63 | 64 | pos = buf; 65 | pos = steprn(pos, end, "%d", 123); 66 | is("1x3 = 3", strlen(buf) == 3); 67 | pos = steprn(pos, end, "%d", 456); 68 | is("2x3 = 6", strlen(buf) == 6); 69 | pos = steprn(pos, end, "%d", 789); 70 | is("3x3 = 9", strlen(buf) == 9); 71 | pos = steprn(pos, end, "%03d", 007); 72 | is("4x3 = 12", strlen(buf) == 12); 73 | pos = steprn(pos, end, "%d", -42); 74 | is("5x3 = 15", strlen(buf) == 15); 75 | pos = steprn(pos, end, "%d", 7890); 76 | is("buffer is full", strlen(buf) == 15); 77 | is("return value is end", pos == end); 78 | pos = steprn(pos, end, "%d", 67890); 79 | is("buffer doesn't get fuller", strlen(buf) == 15); 80 | is("return value is end", pos == end); 81 | 82 | pos = buf; 83 | pos = steprn(pos, end, "%s", "abcdefghijklmnopq"); 84 | is("truncation", strlen(buf) == 15); 85 | is("return value is end", pos == end); 86 | 87 | pos = buf; 88 | pos = steprn(pos, end, "%s%s", "x", "yz"); 89 | pos = steprn(prevpos=pos, end, ""); 90 | is("empty append works", strlen(buf) == 3); 91 | is("return value is unchanged", pos == prevpos); 92 | 93 | 94 | printf("# stecpe\n"); 95 | 96 | pos = buf; 97 | pos = stecpe(pos, end, buf2, buf2 + 6); 98 | is("1x6 = 6", strlen(buf) == 6); 99 | pos = stecpe(pos, end, buf2, buf2 + 6); 100 | is("2x6 = 12", strlen(buf) == 12); 101 | pos = stecpe(pos, end, buf2, buf2 + 6); 102 | is("buffer is full", strlen(buf) == 15); 103 | is("return value is end", pos == end); 104 | 105 | pos = buf; 106 | pos = steprn(pos, end, buf2, buf2 + sizeof buf2); 107 | is("truncation", strlen(buf) == 15); 108 | is("return value is end", pos == end); 109 | 110 | pos = buf; 111 | pos = stecpe(pos, end, buf2, buf2 + 3); 112 | pos = stecpe(prevpos=pos, end, buf2, buf2); 113 | is("empty append works", strlen(buf) == 3); 114 | is("return value is unchanged", pos == prevpos); 115 | 116 | 117 | printf("# stechr\n"); 118 | char *x = stechr(buf2, buf2 + sizeof buf2, 'x'); 119 | is("x found", *x == 'x'); 120 | is("x is at buf2[6]", x == buf2 + 6); 121 | char *w = stechr(buf2, buf2 + sizeof buf2, 'w'); 122 | is("w not found", *w == 0); 123 | is("returned end of string instead", w == buf2 + strlen(buf2)); 124 | char *y = stechr(buf2, buf2 + 6, 'y'); 125 | is("y not found in first 6 chars", y == buf2 + 6); 126 | 127 | 128 | printf("# steccpy\n"); 129 | pos = buf; 130 | pos = steccpy(pos, end, "abc,def", ','); 131 | is("1x3 = 3", strlen(buf) == 3); 132 | pos = steccpy(pos, end, "def:ghijkl", ':'); 133 | is("2x3 = 6", strlen(buf) == 6); 134 | pos = steccpy(pos, end, ":ghi", ':'); 135 | pos = steccpy(pos, end, "ghi", ':'); 136 | is("3x3 = 9", strlen(buf) == 9); 137 | pos = steccpy(pos, end, "jkl", '\0'); 138 | is("4x3 = 12", strlen(buf) == 12); 139 | pos = steccpy(pos, end, "mnopqst", '!'); 140 | is("5x3 = 15", strlen(buf) == 15); 141 | pos = steccpy(pos, end, "full", '!'); 142 | is("buffer is full", strlen(buf) == 15); 143 | is("return value is end", pos == end); 144 | pos = steccpy(pos, end, "fuller", 'r'); 145 | is("buffer doesn't get fuller", strlen(buf) == 15); 146 | is("return value is end", pos == end); 147 | 148 | 149 | return status; 150 | } 151 | --------------------------------------------------------------------------------