/*
 * Replacements for common but usually nonstandard functions that aren't
 * supplied by all platforms.
 *
 * Copyright (C) 2009 by Dan Fandrich <dan@coneharvesters.com>, et. al.
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */
#include "libbb.h"

#ifndef HAVE_STRCHRNUL
char* FAST_FUNC strchrnul(const char *s, int c)
{
	while (*s != '\0' && *s != c)
		s++;
	return (char*)s;
}
#endif

#ifndef HAVE_USLEEP
int FAST_FUNC usleep(unsigned usec)
{
	struct timespec ts;
	ts.tv_sec = usec / 1000000u;
	ts.tv_nsec = (usec % 1000000u) * 1000u;
	/*
	 * If a signal has non-default handler, nanosleep returns early.
	 * Our version of usleep doesn't return early
	 * if interrupted by such signals:
	 */
	while (nanosleep(&ts, &ts) != 0)
		continue;
	return 0;
}
#endif

#ifndef HAVE_VASPRINTF
int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p)
{
	int r;
	va_list p2;
	char buf[128];

	va_copy(p2, p);
	r = vsnprintf(buf, 128, format, p);
	va_end(p);

	/* Note: can't use xstrdup/xmalloc, they call vasprintf (us) on failure! */

	if (r < 128) {
		va_end(p2);
		*string_ptr = strdup(buf);
		return (*string_ptr ? r : -1);
	}

	*string_ptr = malloc(r+1);
	r = (*string_ptr ? vsnprintf(*string_ptr, r+1, format, p2) : -1);
	va_end(p2);

	return r;
}
#endif

#ifndef HAVE_DPRINTF
/* dprintf is now part of POSIX.1, but was only added in 2008 */
int dprintf(int fd, const char *format, ...)
{
	va_list p;
	int r;
	char *string_ptr;

	va_start(p, format);
	r = vasprintf(&string_ptr, format, p);
	va_end(p);
	if (r >= 0) {
		r = full_write(fd, string_ptr, r);
		free(string_ptr);
	}
	return r;
}
#endif

#ifndef HAVE_MEMRCHR
/* Copyright (C) 2005 Free Software Foundation, Inc.
 * memrchr() is a GNU function that might not be available everywhere.
 * It's basically the inverse of memchr() - search backwards in a
 * memory block for a particular character.
 */
void* FAST_FUNC memrchr(const void *s, int c, size_t n)
{
	const char *start = s, *end = s;

	end += n - 1;

	while (end >= start) {
		if (*end == (char)c)
			return (void *) end;
		end--;
	}

	return NULL;
}
#endif

#ifndef HAVE_MKDTEMP
/* This is now actually part of POSIX.1, but was only added in 2008 */
char* FAST_FUNC mkdtemp(char *template)
{
	/* NB: on error, mktemp returns an empty string, not NULL */
	if (mktemp(template)[0] == '\0' || mkdir(template, 0700) != 0)
		return NULL;
	return template;
}
#endif

#ifndef HAVE_STRCASESTR
/* Copyright (c) 1999, 2000 The ht://Dig Group */
char* FAST_FUNC strcasestr(const char *s, const char *pattern)
{
	int length = strlen(pattern);

	while (*s) {
		if (strncasecmp(s, pattern, length) == 0)
			return (char *)s;
		s++;
	}
	return 0;
}
#endif

#ifndef HAVE_STRSEP
/* Copyright (C) 2004 Free Software Foundation, Inc. */
char* FAST_FUNC strsep(char **stringp, const char *delim)
{
	char *start = *stringp;
	char *ptr;

	if (!start)
		return NULL;

	if (!*delim)
		ptr = start + strlen(start);
	else {
		ptr = strpbrk(start, delim);
		if (!ptr) {
			*stringp = NULL;
			return start;
		}
	}

	*ptr = '\0';
	*stringp = ptr + 1;

	return start;
}
#endif

#ifndef HAVE_STPCPY
char* FAST_FUNC stpcpy(char *p, const char *to_add)
{
	while ((*p = *to_add) != '\0') {
		p++;
		to_add++;
	}
	return p;
}
#endif

#ifndef HAVE_STPNCPY
char* FAST_FUNC stpncpy(char *p, const char *to_add, size_t n)
{
	while (n != 0 && (*p = *to_add) != '\0') {
		p++;
		to_add++;
		n--;
	}
	return p;
}
#endif

#ifndef HAVE_GETLINE
ssize_t FAST_FUNC getline(char **lineptr, size_t *n, FILE *stream)
{
	int ch;
	char *line = *lineptr;
	size_t alloced = *n;
	size_t len = 0;

	do {
		ch = fgetc(stream);
		if (ch == EOF)
			break;
		if (len + 1 >= alloced) {
			alloced += alloced/4 + 64;
			line = xrealloc(line, alloced);
		}
		line[len++] = ch;
	} while (ch != '\n');

	if (len == 0)
		return -1;

	line[len] = '\0';
	*lineptr = line;
	*n = alloced;
	return len;
}
#endif

#ifndef HAVE_TTYNAME_R
int ttyname_r(int fd, char *buf, size_t buflen)
{
	int r;
	char path[sizeof("/proc/self/fd/%d") + sizeof(int)*3];

	if (!isatty(fd))
		return errno == EINVAL ? ENOTTY : errno;
	sprintf(path, "/proc/self/fd/%d", fd);
	r = readlink(path, buf, buflen);
	if (r < 0)
		return errno;
	if (r >= buflen)
		return ERANGE;
	buf[r] = '\0';
	return 0;
}
#endif
