Skip to content

Instantly share code, notes, and snippets.

@aktau
Created August 31, 2014 21:11
Show Gist options
  • Save aktau/134bf081496fe210b544 to your computer and use it in GitHub Desktop.
Save aktau/134bf081496fe210b544 to your computer and use it in GitHub Desktop.
neovim: path building API tryouts
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#define MAXPATHL 128
/// like `strcat`, but returns a pointer to the '\0' terminator of rht
/// resulting string.
///
/// @see {strcat}
char *xstpcat(char *restrict dst, const char *restrict src) {
return stpcpy(dst + strlen(dst), src);
}
char *strjoin(char *restrict str, const char *restrict last, const char *restrict sep) {
return stpcpy(xstpcat(str, sep), last);
}
/// a memcpy that works like stpcpy, returning the position after the last
/// copied position, making it more useful.
static inline void *xmempcpy(void *restrict dst, const void *restrict src, size_t num) {
return ((char *) memcpy(dst, src, num)) + num;
}
/// like `stpncpy`, except that it doesn't zero-fill the array. A bit like
/// the difference between `strncpy` and `strlcpy`.
char *stplcpy(char *restrict dst, const char *restrict src, size_t maxlen) {
const char *p = memchr(src, '\0', maxlen);
return xmempcpy(dst, src, p ? (p - src) : maxlen);
}
/// append `tail` to `str` if `str` doesn't already end in `tail`.
///
/// @note `str` must have enough space for `str_len` + `tail_len` bytes.
/// @return a pointer to the '\0' byte at the end of the string
void *memapp(char *restrict str, size_t str_len, const char *restrict tail, size_t tail_len) {
char *end = str + str_len;
if (str_len >= tail_len && memcmp(end - tail_len, tail, tail_len) == 0) {
return str + str_len;
}
return stpcpy(end, tail);
}
/// like `stpcat`, but only appends `tail` when `str` doesn't already end in
/// `tail`. Returns the end of the resulting string.
/// @see {stpcat}
char *strapp(char *restrict str, const char *restrict tail) {
return memapp(str, strlen(str), tail, strlen(tail));
}
/// like `strjoin`, but doesn't add `sep` if `str` already ends in `sep`
char *strcjoin(char *restrict str, const char *restrict last, const char *restrict sep) {
return stpcpy(strapp(str, sep), last);
}
/// strcjoinv, join a variable number of strings with `sep` if they don't
/// already end in `sep`.
///
/// @note to @oni-link, I'm sure you'll notice that this code doesn't
/// gracefully handle the case where the we just managed to fill up
/// `buf_len` with a valid string. The function as it is right now will
/// signal truncation, while that's not necessary. Very shameful, I know,
/// but I'm thinking about how to elegantly put that in.
///
/// @example
/// char buf[MAXPATHL];
/// strcjoinv(buf, sizeof(buf), "/", "my/", "concatenated", "path");
///
/// @see {strjoinv}
/// @return a pointer to the NUL byte at the end of the resulting string, or
/// NULL if there was insufficient space.
char *strcjoinv(char *restrict buf, size_t buf_len, const char *restrict sep, ...) {
va_list arglist;
va_start(arglist, sep);
size_t sep_len = strlen(sep);
size_t buf_written = 0;
const char *p;
while ((p = va_arg(arglist, const char *)) != NULL) {
const char *start = buf;
buf = stplcpy(buf, p, buf_len - buf_written);
// calculate how many bytes we've advanced
size_t adv = buf - start;
buf_written += adv;
// if we're (going to) advance too far, NUL-terminate and signal the
// truncation to the caller
if (buf_written + sep_len >= buf_len) {
*buf = '\0';
return NULL;
}
size_t rest_len = 0;
if (adv >= sep_len) {
buf -= sep_len;
rest_len = sep_len;
}
start = buf;
buf = memapp(buf, rest_len, sep, sep_len);
buf_written += buf - start;
}
// remove the last separator, which we copied into the string in all our
// youthful vigour. Alas, 'twas not to be, dear final separator.
if (buf_written > 0) {
buf[-sep_len] = '\0';
}
va_end(arglist);
return buf;
}
int main() {
char buf[256];
bool stupid = true;
bool condjoin = true;
if (stupid) {
if (condjoin) {
printf("running stupid strcjoin\n");
strcjoin(buf, "moo/kit/", "/");
strcjoin(buf, "mookit", "/");
strcjoin(buf, "mookit", "/");
}
else {
printf("running stupid strjoin\n");
strjoin(buf, "moo/kit/", "/");
strjoin(buf, "mookit", "/");
strjoin(buf, "mookit", "/");
}
}
else {
char *tmp = buf;
if (condjoin) {
printf("running smart strcjoin\n");
tmp = strcjoin(tmp, "moo/kit/", "/");
tmp = strcjoin(tmp, "mookit", "/");
tmp = strcjoin(tmp, "mookit", "/");
}
else {
printf("running smart strjoin\n");
tmp = strjoin(tmp, "moo/kit/", "/");
tmp = strjoin(tmp, "mookit", "/");
tmp = strjoin(tmp, "mookit", "/");
}
}
printf("str(c)join(s): %s\n", buf);
char b[MAXPATHL];
char *end = strcjoinv(b, sizeof(b), "/", "cool", "path", "well", "that's",
"nice", "path5", "path6//", "path7/", "path8/", "path9/", "path10",
/* "pathxxxxxxxxxxxxxxxxxxxxxxxxx11", */
/* "pathxxxxxxxxxxxxxxxxxxxxxxxxx12", */
"path13", NULL);
if (!end) {
printf("got truncated, sad days");
return EXIT_FAILURE;
}
printf("strcjoinv(...): %s (len: %zu)\n", b, strlen(b));
if (!strcjoinv(b, sizeof(b), "●", "a", "special", "sep", NULL)) {
printf("got truncated, sad days");
return EXIT_FAILURE;
}
printf("strcjoinv(...): %s (len: %zu)\n", b, strlen(b));
if (!strcjoinv(b, sizeof(b), "\\", "some", "windows", "path", NULL)) {
printf("got truncated, sad days");
return EXIT_FAILURE;
}
printf("strcjoinv(...): %s (len: %zu)\n", b, strlen(b));
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment