#include <u.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <errno.h>
#include <fcntl.h>
#include <libc.h>
#include "rc.h"
#include "exec.h"
#include "io.h"
#include "fns.h"
#include "getflags.h"

extern char **mkargv(word*);
extern int mapfd(int);

static char *eargs = "cdflmnstuv";
static int rlx[] = {
	RLIMIT_CORE,
	RLIMIT_DATA,
	RLIMIT_FSIZE,
#ifdef RLIMIT_MEMLOCK
	RLIMIT_MEMLOCK,
#else
	0,
#endif
#ifdef RLIMIT_RSS
	RLIMIT_RSS,
#else
	0,
#endif
	RLIMIT_NOFILE,
	RLIMIT_STACK,
	RLIMIT_CPU,
#ifdef RLIMIT_NPROC
	RLIMIT_NPROC,
#else
	0,
#endif
#ifdef RLIMIT_RSS
	RLIMIT_RSS,
#else
	0,
#endif
};

static void
eusage(void)
{
	fprint(mapfd(2), "usage: ulimit [-SHa%s [limit]]\n", eargs);
}

#define Notset -4
#define Unlimited -3
#define Hard -2
#define Soft -1

void
execulimit(void)
{
	rlim_t n;
	int fd, argc, sethard, setsoft, limit;
	int flag[256];
	char **argv, **oargv, *p;
	char *argv0;
	struct rlimit rl;

	argv0 = nil;
	setstatus("");
	oargv = mkargv(runq->argv->words);
	argv = oargv+1;
	for(argc=0; argv[argc]; argc++)
		;

	memset(flag, 0, sizeof flag);
	ARGBEGIN{
	default:
		if(strchr(eargs, ARGC()) == nil){
			eusage();
			return;
		}
	case 'S':
	case 'H':
	case 'a':
		flag[ARGC()] = 1;
		break;
	}ARGEND

	if(argc > 1){
		eusage();
		goto out;
	}

	fd = mapfd(1);

	sethard = 1;
	setsoft = 1;
	if(flag['S'] && flag['H'])
		;
	else if(flag['S'])
		sethard = 0;
	else if(flag['H'])
		setsoft = 0;

	limit = Notset;
	if(argc>0){
		if(strcmp(argv[0], "unlimited") == 0)
			limit = Unlimited;
		else if(strcmp(argv[0], "hard") == 0)
			limit = Hard;
		else if(strcmp(argv[0], "soft") == 0)
			limit = Soft;
		else if((limit = strtol(argv[0], &p, 0)) < 0 || *p != 0){
			eusage();
			goto out;
		}
	}
	if(flag['a']){
		for(p=eargs; *p; p++){
			getrlimit(rlx[p-eargs], &rl);
			n = flag['H'] ? rl.rlim_max : rl.rlim_cur;
			if(n == RLIM_INFINITY)
				fprint(fd, "ulimit -%c unlimited\n", *p);
			else
				fprint(fd, "ulimit -%c %llud\n", *p, (uvlong)n);
		}
		goto out;
	}
	for(p=eargs; *p; p++){
		if(flag[(uchar)*p]){
			n = 0;
			getrlimit(rlx[p-eargs], &rl);
			switch(limit){
			case Notset:
				n = flag['H'] ? rl.rlim_max : rl.rlim_cur;
				if(n == RLIM_INFINITY)
					fprint(fd, "ulimit -%c unlimited\n", *p);
				else
					fprint(fd, "ulimit -%c %llud\n", *p, (uvlong)n);
				break;
			case Hard:
				n = rl.rlim_max;
				goto set;
			case Soft:
				n = rl.rlim_cur;
				goto set;
			case Unlimited:
				n = RLIM_INFINITY;
				goto set;
			default:
				n = limit;
			set:
				if(setsoft)
					rl.rlim_cur = n;
				if(sethard)
					rl.rlim_max = n;
				if(setrlimit(rlx[p-eargs], &rl) < 0)
					fprint(mapfd(2), "setrlimit: %r\n");
			}
		}
	}

out:
	free(oargv);
	poplist();
	flush(err);
}

void
execumask(void)
{
	int n, argc;
	char **argv, **oargv, *p;
	char *argv0;

	argv0 = nil;
	setstatus("");
	oargv = mkargv(runq->argv->words);
	argv = oargv+1;
	for(argc=0; argv[argc]; argc++)
		;

	ARGBEGIN{
	default:
	usage:
		fprint(mapfd(2), "usage: umask [mode]\n");
		goto out;
	}ARGEND

	if(argc > 1)
		goto usage;

	if(argc == 1){
		n = strtol(argv[0], &p, 8);
		if(*p != 0 || p == argv[0])
			goto usage;
		umask(n);
		goto out;
	}

	n = umask(0);
	umask(n);
	if(n < 0){
		fprint(mapfd(2), "umask: %r\n");
		goto out;
	}

	fprint(mapfd(1), "umask %03o\n", n);

out:
	free(oargv);
	poplist();
	flush(err);
}

/*
 * Cope with non-blocking read.
 */
long
readnb(int fd, char *buf, long cnt)
{
	int n, didreset;
	int flgs;

	didreset = 0;
again:
	n = read(fd, buf, cnt);
	if(n == -1)
	if(errno == EAGAIN){
		if(!didreset){
			if((flgs = fcntl(fd, F_GETFL, 0)) == -1)
				return -1;
			flgs &= ~O_NONBLOCK;
			if(fcntl(fd, F_SETFL, flgs) == -1)
				return -1;
			didreset = 1;
		}
		goto again;
	}

	return n;
}
