aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--README60
-rw-r--r--example.c44
-rw-r--r--libste.3115
-rw-r--r--ste.h4
-rw-r--r--stechr.c8
-rw-r--r--stecpe.c21
-rw-r--r--stecpy.c17
-rw-r--r--steprn.c23
-rw-r--r--tests.c125
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;
+}