From 75ea20675d012e692996bc6beae9149b0deb08b0 Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Fri, 5 Nov 2021 18:52:11 +0100 Subject: initial commit --- Makefile | 20 ++++++++++ README | 60 ++++++++++++++++++++++++++++++ example.c | 44 ++++++++++++++++++++++ libste.3 | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ste.h | 4 ++ stechr.c | 8 ++++ stecpe.c | 21 +++++++++++ stecpy.c | 17 +++++++++ steprn.c | 23 ++++++++++++ tests.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 437 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 example.c create mode 100644 libste.3 create mode 100644 ste.h create mode 100644 stechr.c create mode 100644 stecpe.c create mode 100644 stecpy.c create mode 100644 steprn.c create mode 100644 tests.c 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 + + 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 + +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 +#include +#include +#include + +#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 +#include + +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 + +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 +#include + +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 +#include + +#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; +} -- cgit 1.4.1