diff options
author | Leah Neukirchen <leah@vuxu.org> | 2021-11-05 18:52:11 +0100 |
---|---|---|
committer | Leah Neukirchen <leah@vuxu.org> | 2021-11-05 18:52:46 +0100 |
commit | 75ea20675d012e692996bc6beae9149b0deb08b0 (patch) | |
tree | ab1569403e1c5f8e76f5f93f969a3335275c54a5 | |
download | libste-75ea20675d012e692996bc6beae9149b0deb08b0.tar.gz libste-75ea20675d012e692996bc6beae9149b0deb08b0.tar.xz libste-75ea20675d012e692996bc6beae9149b0deb08b0.zip |
initial commit
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | README | 60 | ||||
-rw-r--r-- | example.c | 44 | ||||
-rw-r--r-- | libste.3 | 115 | ||||
-rw-r--r-- | ste.h | 4 | ||||
-rw-r--r-- | stechr.c | 8 | ||||
-rw-r--r-- | stecpe.c | 21 | ||||
-rw-r--r-- | stecpy.c | 17 | ||||
-rw-r--r-- | steprn.c | 23 | ||||
-rw-r--r-- | tests.c | 125 |
10 files changed, 437 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0be968b --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +all: libste.a example + +libste.a: stechr.o stecpe.o stecpy.o steprn.o + $(AR) $(ARFLAGS) $@ $^ + +example: example.o libste.a + +tests: tests.o libste.a + +check: tests FRC + prove -v ./tests + +README: libste.3 + mandoc -Tutf8 $^ | col -bx >$@ + + +clean: FRC + rm -f *.o *.a example tests + +FRC: diff --git a/README b/README new file mode 100644 index 0000000..26bf455 --- /dev/null +++ b/README @@ -0,0 +1,60 @@ +LIBSTE(3) Library Functions Manual LIBSTE(3) + +NAME + stecpy, stecpe, stechr, steprn – string library based on string ends + +SYNOPSIS + #include <ste.h> + + char * + stecpy(char *dst, char *end, const char *src); + + char * + stecpe(char *dst, char *dstend, const char *src, const char *srcend); + + char * + stechr(const char *src, const char *end, int c); + + char * + steprn(char *dst, char *end, const char *fmt, ...); + +DESCRIPTION + libste provides four useful functions for dealing with strings. + + stecpy copies the NUL-terminated string src to dst, but writes no + characters beyond end. If any characters are copied, dst will be NUL- + terminated and the return value is a pointer to the NUL byte. On + truncation, end is returned. + + stecpe copies the string between src and srcend to dst, but writes no + characters beyond dstend. If any characters are copied, dst will be NUL- + terminated and the return value is a pointer to the NUL byte. On + truncation, end is returned. + + stechr returns a pointer to the first occurence of c (converted to a + char) in the NUL-terminated string pointed to by src, but reads no + characters beyond end. If c is not found, stecpy returns a pointer to + the first NUL byte in src, or end if none was found. + + steprn uses vsnprintf(3) to write formatted output to dst, but writes no + characters beyond end. If any characters are written, dst will be NUL- + terminated and the return value is a pointer to the NUL byte. On + truncation, end is returned. + + Note that it is safe to pass the return value of all functions listed + above as argument for dst when the same end is reused. In this case, the + function call does nothing but return dst again. At any point, + truncation can be checked by comparing the return value to end. + +AUTHORS + Leah Neukirchen <leah@vuxu.org> + +LICENSE + libste is in the public domain. + + To the extent possible under law, the creator of this work has waived all + copyright and related or neighboring rights to this work. + + http://creativecommons.org/publicdomain/zero/1.0/ + +Void Linux November 5, 2021 Void Linux diff --git a/example.c b/example.c new file mode 100644 index 0000000..e9f57d5 --- /dev/null +++ b/example.c @@ -0,0 +1,44 @@ +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ste.h" + +/* example of libste usage: iterate over $PATH and append argv[1], + print the entries that fit into PATH_MAX. */ + +int +main(int argc, char *argv[]) +{ + char *path = getenv("PATH"); + if (!path) + path = ""; + + char *program = argc > 1 ? argv[1] : "xyzzy"; + + char *pathend = path + strlen(path); + + char buf[PATH_MAX]; + char *bufend = buf + sizeof buf; + + while (1) { + char *pos = buf; + + char *colon = stechr(path, pathend, ':'); + if (path == colon) /* empty entry */ + pos = stecpy(buf, bufend, "."); + else + pos = stecpe(buf, bufend, path, colon); + + pos = steprn(pos, bufend, "/%s", program); + + if (pos < bufend) { /* no trunaction */ + printf("%s\n", buf); + } + + if (colon == pathend) + break; + path = colon + 1; + } +} diff --git a/libste.3 b/libste.3 new file mode 100644 index 0000000..24b3d4a --- /dev/null +++ b/libste.3 @@ -0,0 +1,115 @@ +.Dd November 5, 2021 +.Dt LIBSTE 3 +.Os +.Sh NAME +.Nm stecpy , +.Nm stecpe , +.Nm stechr , +.Nm steprn +.Nd string library based on string ends +.Sh SYNOPSIS +.In ste.h +.Ft "char *" +.Fn stecpy "char *dst" "char *end" "const char *src" +.Ft "char *" +.Fn stecpe "char *dst" "char *dstend" "const char *src" "const char *srcend" +.Ft "char *" +.Fn stechr "const char *src" "const char *end" "int c" +.Ft "char *" +.Fn steprn "char *dst" "char *end" "const char *fmt" "..." +.Sh DESCRIPTION +.Nm libste +provides four useful functions for dealing with strings. +.Pp +.Nm stecpy +copies the NUL-terminated string +.Fa src +to +.Fa dst , +but writes no characters beyond +.Fa end . +If any characters are copied, +.Fa dst +will be NUL-terminated +and the return value is a pointer to the NUL byte. +On truncation, +.Fa end +is returned. +.Pp +.Nm stecpe +copies the string between +.Fa src +and +.Fa srcend +to +.Fa dst , +but writes no characters beyond +.Fa dstend . +If any characters are copied, +.Fa dst +will be NUL-terminated +and the return value is a pointer to the NUL byte. +On truncation, +.Fa end +is returned. +.Pp +.Nm stechr +returns a pointer to the first occurence of +.Fa c +.Pq converted to a Vt char +in the NUL-terminated string pointed to by +.Fa src , +but reads no characters beyond +.Fa end . +If +.Fa c +is not found, +.Nm +returns a pointer to the first NUL byte in +.Fa src , +or +.Fa end +if none was found. +.Pp +.Nm steprn +uses +.Xr vsnprintf 3 +to write formatted output to +.Fa dst , +but writes no characters beyond +.Fa end . +If any characters are written, +.Fa dst +will be NUL-terminated +and the return value is a pointer to the NUL byte. +On truncation, +.Fa end +is returned. +.Pp +Note that it is safe to pass the return value of all functions listed above +as argument for +.Fa dst +when the same +.Fa end +is reused. +In this case, the function call does nothing but return +.Fa dst +again. +At any point, truncation can be checked by comparing the return value to +.Fa end . +.\" .Sh RETURN VALUES +.\" .Sh SEE ALSO +.\" .Sh STANDARDS +.\" .Sh HISTORY +.Sh AUTHORS +.An Leah Neukirchen Aq Mt leah@vuxu.org +.Sh LICENSE +.Nm libste +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/ste.h b/ste.h new file mode 100644 index 0000000..b2ff64e --- /dev/null +++ b/ste.h @@ -0,0 +1,4 @@ +char *stecpy(char *dst, char *end, const char *src); +char *stecpe(char *dst, char *end, const char *src, const char *srcend); +char *stechr(const char *src, const char *end, int c); +char *steprn(char *dst, char *end, const char *fmt, ...); diff --git a/stechr.c b/stechr.c new file mode 100644 index 0000000..d5bce2f --- /dev/null +++ b/stechr.c @@ -0,0 +1,8 @@ +char * +stechr(const char *src, const char *end, int c) +{ + while (src < end && *src && *(unsigned char *)src != (unsigned char)c) + src++; + + return (char *)src; +} diff --git a/stecpe.c b/stecpe.c new file mode 100644 index 0000000..0cfbaad --- /dev/null +++ b/stecpe.c @@ -0,0 +1,21 @@ +#include <stddef.h> +#include <string.h> + +char * +stecpe(char *dst, const char *end, const char *src, const char *srcend) +{ + if (dst >= end) + return dst; + + size_t l = end - dst - 1; + size_t t = 1; + if (srcend - src < l) { + l = srcend - src; + t = 0; + } + + memcpy(dst, src, l); + dst[l] = 0; + + return dst + l + t; +} diff --git a/stecpy.c b/stecpy.c new file mode 100644 index 0000000..02b69c6 --- /dev/null +++ b/stecpy.c @@ -0,0 +1,17 @@ +#include <stddef.h> + +char * +stecpy(char *dst, const char *end, const char *src) +{ + if (dst >= end) + return dst; + + size_t n = end - dst; + while (n && (*dst = *src)) + n--, src++, dst++; + + if (dst == end) + dst[-1] = 0; + + return dst; +} diff --git a/steprn.c b/steprn.c new file mode 100644 index 0000000..2ca0a27 --- /dev/null +++ b/steprn.c @@ -0,0 +1,23 @@ +#include <stdarg.h> +#include <stdio.h> + +char * +steprn(char *dst, char *end, const char *fmt, ...) +{ + if (dst >= end) + return end; + + va_list ap; + va_start(ap, fmt); + int r = vsnprintf(dst, end - dst, fmt, ap); + va_end(ap); + + if (r < 0) { + /* snprintf only fails for silly reasons: + truncate what was written, behave as noop. */ + *dst = 0; + return dst; + } + + return r > end - dst ? end : dst + r; +} diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..9255a70 --- /dev/null +++ b/tests.c @@ -0,0 +1,125 @@ +#include <stdio.h> +#include <string.h> + +#include "ste.h" + +static int status; + +void +is(char *desc, int ok) +{ + if (!ok) + status = 1; + printf("%s - %s\n", ok ? "ok" : "not ok", desc); +} + +int +main() +{ + printf("1..39\n"); + + printf("# stecpy\n"); + + char buf[16]; + char *end = buf + sizeof buf; + char buf2[32] = "stringxyzxyzxyzxyzxyz"; + char *pos, *prevpos; + + pos = buf; + pos = stecpy(pos, end, "abc"); + is("1x3 = 3", strlen(buf) == 3); + pos = stecpy(pos, end, "def"); + is("2x3 = 6", strlen(buf) == 6); + pos = stecpy(pos, end, "ghi"); + is("3x3 = 9", strlen(buf) == 9); + pos = stecpy(pos, end, "jkl"); + is("4x3 = 12", strlen(buf) == 12); + pos = stecpy(pos, end, "mno"); + is("5x3 = 15", strlen(buf) == 15); + pos = stecpy(pos, end, "full"); + is("buffer is full", strlen(buf) == 15); + is("return value is end", pos == end); + pos = stecpy(pos, end, "fuller"); + is("buffer doesn't get fuller", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = stecpy(pos, end, "abcdefghijklmnopq"); + is("truncation", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = stecpy(pos, end, "xyz"); + pos = stecpy(prevpos=pos, end, ""); + is("empty append works", strlen(buf) == 3); + is("return value is unchanged", pos == prevpos); + + + printf("# steprn\n"); + + pos = buf; + pos = steprn(pos, end, "%d", 123); + is("1x3 = 3", strlen(buf) == 3); + pos = steprn(pos, end, "%d", 456); + is("2x3 = 6", strlen(buf) == 6); + pos = steprn(pos, end, "%d", 789); + is("3x3 = 9", strlen(buf) == 9); + pos = steprn(pos, end, "%03d", 007); + is("4x3 = 12", strlen(buf) == 12); + pos = steprn(pos, end, "%d", -42); + is("5x3 = 15", strlen(buf) == 15); + pos = steprn(pos, end, "%d", 7890); + is("buffer is full", strlen(buf) == 15); + is("return value is end", pos == end); + pos = steprn(pos, end, "%d", 67890); + is("buffer doesn't get fuller", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = steprn(pos, end, "%s", "abcdefghijklmnopq"); + is("truncation", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = steprn(pos, end, "%s%s", "x", "yz"); + pos = steprn(prevpos=pos, end, ""); + is("empty append works", strlen(buf) == 3); + is("return value is unchanged", pos == prevpos); + + + printf("# stecpe\n"); + + pos = buf; + pos = stecpe(pos, end, buf2, buf2 + 6); + is("1x6 = 6", strlen(buf) == 6); + pos = stecpe(pos, end, buf2, buf2 + 6); + is("2x6 = 12", strlen(buf) == 12); + pos = stecpe(pos, end, buf2, buf2 + 6); + is("buffer is full", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = steprn(pos, end, buf2, buf2 + sizeof buf2); + is("truncation", strlen(buf) == 15); + is("return value is end", pos == end); + + pos = buf; + pos = stecpe(pos, end, buf2, buf2 + 3); + pos = stecpe(prevpos=pos, end, buf2, buf2); + is("empty append works", strlen(buf) == 3); + is("return value is unchanged", pos == prevpos); + + + printf("# stechr\n"); + char *x = stechr(buf2, buf2 + sizeof buf2, 'x'); + is("x found", *x == 'x'); + is("x is at buf2[6]", x == buf2 + 6); + char *w = stechr(buf2, buf2 + sizeof buf2, 'w'); + is("w not found", *w == 0); + is("returned end of string instead", w == buf2 + strlen(buf2)); + char *y = stechr(buf2, buf2 + 6, 'y'); + is("y not found in first 6 chars", y == buf2 + 6); + + + return status; +} |