/*-
 * Copyright © 2019
 *	mirabilos <m@mirbsd.org>
 * Copyright © 2018
 *	mirabilos <t.glaser@tarent.de>
 * The copyright notices and licences of the files in .linked/ inclu‐
 * ded below shall be considered being part of this copyright notice.
 * Also contains material part of “jupp” (Joe’s Own Editor), © 2018
 *	mirabilos <m@mirbsd.org>
 * Contains code from “mksh” (The MirBSD Korn Shell) © 2015
 *	mirabilos <m@mirbsd.org>
 *	KO Myung-Hun <komh@chollian.net>
 *
 * Provided that these terms and disclaimer and all copyright notices
 * are retained or reproduced in an accompanying document, permission
 * is granted to deal in this work without restriction, including un‐
 * limited rights to use, publicly perform, distribute, sell, modify,
 * merge, give away, or sublicence.
 *
 * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
 * the utmost extent permitted by applicable law, neither express nor
 * implied; without malicious intent or gross negligence. In no event
 * may a licensor, author or contributor be held liable for indirect,
 * direct, other damage, loss, or other issues arising in any way out
 * of dealing in the work, even if advised of the possibility of such
 * damage or existence of a defect, except proven that it results out
 * of said person’s immediate fault when using the work as intended.
 */

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "compat.h"
#define PAX_JUST_THE_WARNINGS
#include "extern.h"

__RCSID("$MirOS: src/bin/pax/compat.c,v 1.5 2019/02/23 23:24:54 tg Exp $");

int
binopen3(int features, const char *path, int flags, mode_t mode)
{
	int fd;

#ifdef O_BINARY
	flags |= O_BINARY;
#endif
#ifdef O_CLOEXEC
	if (features & BO_CLEXEC) {
		flags |= O_CLOEXEC;
		features &= ~BO_CLEXEC;
	}
#endif
#ifdef O_DIRECTORY
	if (features & BO_MAYBE_DIR)
		flags |= O_DIRECTORY;
#endif

	if ((fd = (features & BO__TWO) ? open(path, flags) :
	    open(path, flags, mode)) != -1) {
		if ((features & BO_CLEXEC) &&
		    fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
			syswarn(0, errno,
			    "Failed to set the close-on-exec flag");
	}
	return (fd);
}

ssize_t
dwrite(int fd, const void *data, size_t size)
{
	const unsigned char *buf = data;
	ssize_t rv = 0, z;

	while (size) {
		if ((z = write(fd, buf, size)) < 0) {
			if (errno == EINTR)
				continue;
			return (rv ? /* fucked up since we got some */ -2 : -1);
		}
		rv += z;
		buf += z;
		size -= z;
	}
	return (rv);
}

void
strmode(mode_t mode, char *p)
{
	/* print type */
	switch (mode & S_IFMT) {
	case S_IFDIR:			/* directory */
		*p++ = 'd';
		break;
	case S_IFCHR:			/* character special */
		*p++ = 'c';
		break;
	case S_IFBLK:			/* block special */
		*p++ = 'b';
		break;
	case S_IFREG:			/* regular */
		*p++ = '-';
		break;
	case S_IFLNK:			/* symbolic link */
		*p++ = 'l';
		break;
	case S_IFSOCK:			/* socket */
		*p++ = 's';
		break;
#ifdef S_IFIFO
	case S_IFIFO:			/* FIFO */
		*p++ = 'p';
		break;
#endif
	default:			/* unknown */
		*p++ = '?';
		break;
	}
	/* usr */
	if (mode & S_IRUSR)
		*p++ = 'r';
	else
		*p++ = '-';
	if (mode & S_IWUSR)
		*p++ = 'w';
	else
		*p++ = '-';
	switch (mode & (S_IXUSR | S_ISUID)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXUSR:
		*p++ = 'x';
		break;
	case S_ISUID:
		*p++ = 'S';
		break;
	case S_IXUSR | S_ISUID:
		*p++ = 's';
		break;
	}
	/* group */
	if (mode & S_IRGRP)
		*p++ = 'r';
	else
		*p++ = '-';
	if (mode & S_IWGRP)
		*p++ = 'w';
	else
		*p++ = '-';
	switch (mode & (S_IXGRP | S_ISGID)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXGRP:
		*p++ = 'x';
		break;
	case S_ISGID:
		*p++ = 'S';
		break;
	case S_IXGRP | S_ISGID:
		*p++ = 's';
		break;
	}
	/* other */
	if (mode & S_IROTH)
		*p++ = 'r';
	else
		*p++ = '-';
	if (mode & S_IWOTH)
		*p++ = 'w';
	else
		*p++ = '-';
	switch (mode & (S_IXOTH | S_ISVTX)) {
	case 0:
		*p++ = '-';
		break;
	case S_IXOTH:
		*p++ = 'x';
		break;
	case S_ISVTX:
		*p++ = 'T';
		break;
	case S_IXOTH | S_ISVTX:
		*p++ = 't';
		break;
	}
	*p++ = ' ';		/* will be a '+' if ACLs implemented */
	*p = '\0';
}

#define INVALID 	1
#define TOOSMALL 	2
#define TOOLARGE 	3

long long
strtonum(const char *numstr, long long minval, long long maxval,
    const char **errstrp)
{
	long long ll = 0;
	char *ep;
	int error = 0;
	struct errval {
		const char *errstr;
		int err;
	} ev[4] = {
		{ NULL,		0 },
		{ "invalid",	EINVAL },
		{ "too small",	ERANGE },
		{ "too large",	ERANGE },
	};

	ev[0].err = errno;
	errno = 0;
	if (minval > maxval)
		error = INVALID;
	else {
		ll = strtoll(numstr, &ep, 0);
		if (numstr == ep || *ep != '\0')
			error = INVALID;
		else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
			error = TOOSMALL;
		else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
			error = TOOLARGE;
	}
	if (errstrp != NULL)
		*errstrp = ev[error].errstr;
	errno = ev[error].err;
	if (error)
		ll = 0;

	return (ll);
}

#undef WIDEC
#define OUTSIDE_OF_LIBKERN
#define L_strlcat
#define L_strlcpy

#ifdef WIDEC
#ifdef OUTSIDE_OF_LIBKERN
#ifdef __WCHAR_TYPE__
typedef __WCHAR_TYPE__ wchar_t;
#else
#include <wchar.h>
#endif
#endif
/* wide character string functions */
#define NUL			L'\0'
#define char_t			wchar_t
#define fn_len			wcslen
#define	fn_cat			wcslcat
#define fn_cpy			wcslcpy
#else
/* (multibyte) string functions */
#define NUL			'\0'
#define char_t			char
#define fn_len			strlen
#define	fn_cat			strlcat
#define fn_cpy			strlcpy
#endif

#ifdef L_strxfrm
#define strlcpy			strxfrm
#define wcslcpy			wcsxfrm
#define L_strlcpy
#endif

extern size_t fn_len(const char_t *);
#ifdef NEED_STRLFUN_PROTOS
size_t fn_cat(char_t *, const char_t *, size_t);
size_t fn_cpy(char_t *, const char_t *, size_t);
#endif

#ifndef __predict_true
#define __predict_true(exp)	(exp)
#define __predict_false(exp)	(exp)
#endif

/*
 * Appends src to string dst of size dlen (unlike strncat, dlen is the
 * full size of dst, not space left).  At most dlen-1 characters
 * will be copied.  Always NUL terminates (unless dlen <= strlen(dst)).
 * Returns strlen(src) + MIN(dlen, strlen(initial dst)), without the
 * trailing NUL byte counted.  If retval >= dlen, truncation occurred.
 */
size_t
fn_cat(char_t *dst, const char_t *src, size_t dlen)
{
	size_t n = 0, slen;

	slen = fn_len(src);
	while (__predict_true(n + 1 < dlen && dst[n] != NUL))
		++n;
	if (__predict_false(dlen == 0 || dst[n] != NUL))
		return (dlen + slen);
	while (__predict_true((slen > 0) && (n < (dlen - 1)))) {
		dst[n++] = *src++;
		--slen;
	}
	dst[n] = NUL;
	return (n + slen);
}

/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */

/*-
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns strlen(src); if retval >= siz, truncation occurred.
 */
size_t
fn_cpy(char_t *dst, const char_t *src, size_t siz)
{
	const char_t *s = src;

	if (__predict_false(siz == 0))
		goto traverse_src;

	/* copy as many chars as will fit */
	while (--siz && (*dst++ = *s++))
		;

	/* not enough room in dst */
	if (__predict_false(siz == 0)) {
		/* safe to NUL-terminate dst since we copied <= siz-1 chars */
		*dst = NUL;
 traverse_src:
		/* traverse rest of src */
		while (*s++)
			;
	}

	/* count does not include NUL */
	return (s - src - 1);
}

#if 0 /* gcc ignored from here; gmake stops ignoring */
endif

USE_WIDEC?=	1

LIB=		libstrlfun.a
OBJS=		strlcpy.o strlcat.o
ifeq (1,$(strip $(USE_WIDEC)))
OBJS+=		wcslcpy.o wcslcat.o
endif
DEFS=		-DOUTSIDE_OF_LIBKERN
DEFS_strlcpy.o=	-DL_strlcpy
DEFS_strlcat.o=	-DL_strlcat
DEFS_wcslcpy.o=	-DL_strlcpy -DWIDEC
DEFS_wcslcat.o=	-DL_strlcat -DWIDEC

all: $(LIB)

$(LIB): $(OBJS)
	ar rc $(LIB) $(OBJS)
	-ranlib $(LIB)

$(OBJS): strlfun.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $(DEFS_$@) -c -o $@ strlfun.c

#endif /* EOF for gmake and gcc */

