| git.druid.rocks | index | druid520 | mp | src/ | util.c |
src/util.c
#define _POSIX_C_SOURCE 200809L
#define _NETBSD_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpx.h"
void
die(const char* msg)
{
fprintf(stderr, "err: %s.\n", msg);
exit(1);
}
long
pathfits(const char* a, const char* b)
{
size_t an = strlen(a);
size_t bn = strlen(b);
if(bn > (size_t)MAXPATH - 16 || an > (size_t)MAXPATH - 16 - bn)
{
return 0;
}
return 1;
}
long
bufcat(char* dst, size_t max, size_t* off, const char* str)
{
size_t n = strlen(str);
if(*off >= max || n >= max - *off)
{
return 0;
}
memcpy(dst + *off, str, n);
*off = *off + n;
dst[*off] = '\0';
return 1;
}
long
pathjoin(char* dst, size_t max, const char* a, const char* b)
{
size_t off = 0;
if(bufcat(dst, max, &off, a) == 0)
{
return 0;
}
if(bufcat(dst, max, &off, "/") == 0)
{
return 0;
}
if(bufcat(dst, max, &off, b) == 0)
{
return 0;
}
return 1;
}
/* returns bytes read (0..max), -1 if path couldn't be opened, or -2 if
* the file holds MORE than max bytes -- fread() alone can't tell "read
* exactly max bytes because that's the whole file" apart from "silently
* clipped a bigger file at max", so every caller that cares about the
* difference needs its own signal. the extra fgetc() after filling buf
* is the cheapest way to ask "is there really nothing left": it returns
* a real byte (not EOF) only if the file is bigger than max. */
long
readfile(const char* path, char* buf, long max)
{
FILE* f;
long n;
int extra;
f = fopen(path, "rb");
if(f == NULL)
{
return -1;
}
n = (long)fread(buf, 1, (size_t)max, f);
extra = fgetc(f);
fclose(f);
if(extra != EOF)
{
return -2;
}
return n;
}
long
writefile(const char* path, const char* buf, long n)
{
FILE* f;
long w;
f = fopen(path, "wb");
if(f == NULL)
{
return -1;
}
w = (long)fwrite(buf, 1, (size_t)n, f);
fclose(f);
if(w != n)
{
return -1;
}
return w;
}
void
setflags(const char* name, const char* extra)
{
char buf[MAXVAL] = {0};
const char* cur;
size_t off = 0;
cur = getenv(name);
if(cur != NULL && cur[0] != '\0')
{
if(bufcat(buf, sizeof(buf), &off, cur) == 0
|| bufcat(buf, sizeof(buf), &off, " ") == 0
|| bufcat(buf, sizeof(buf), &off, extra) == 0)
{
die("target flags too long");
}
}
else
{
if(bufcat(buf, sizeof(buf), &off, extra) == 0)
{
die("target flags too long");
}
}
setenv(name, buf, 1);
}
void
setxenv(const char* name, const char* val)
{
if(val != NULL && val[0] != '\0')
{
setenv(name, val, 1);
}
}
/* like setflags(), but joins with ':' instead of a space -- for PATH-like
* variables (PKG_CONFIG_PATH) rather than flag lists (CFLAGS/LDFLAGS). a
* separate function rather than a parameter on setflags() itself: keeps
* setflags()'s eight existing space-joined call sites untouched. */
void
setpath(const char* name, const char* extra)
{
char buf[MAXVAL] = {0};
const char* cur;
size_t off = 0;
cur = getenv(name);
if(cur != NULL && cur[0] != '\0')
{
if(bufcat(buf, sizeof(buf), &off, extra) == 0
|| bufcat(buf, sizeof(buf), &off, ":") == 0
|| bufcat(buf, sizeof(buf), &off, cur) == 0)
{
die("target flags too long");
}
}
else
{
if(bufcat(buf, sizeof(buf), &off, extra) == 0)
{
die("target flags too long");
}
}
setenv(name, buf, 1);
}
/* scans the trailing NAME=VAL args of a runsh invocation (argv[4..argc))
* for name, returning a pointer to its value or NULL if absent. used to
* find an effective MP_PREFIX/MP_PREFIX_BASE before those args get
* applied to the child's environment further down. */
const char*
argval(int argc, char** argv, const char* name)
{
int ei;
size_t namelen;
namelen = strlen(name);
for(ei = 4; ei < argc; ei = ei + 1)
{
if(strncmp(argv[ei], name, namelen) == 0 && argv[ei][namelen] == '=')
{
return argv[ei] + namelen + 1;
}
}
return NULL;
}