
#include <string.h>

#include "rc.h"
#ifdef RIX
#include <sys/time.h>
#endif

#ifdef SOLARIS
#define __EXTENSIONS__
#endif
#include <sys/resource.h>
#ifdef SOLARIS
#undef __EXTENSIONS__
#endif

/* Berkeley limit support was cleaned up by Paul Haahr. */

typedef struct Suffix Suffix;
struct Suffix {
	const Suffix *next;
	long amount;
	char *name;
};

static const Suffix
	kbsuf = { NULL, 1024, "k" },
	mbsuf = { &kbsuf, 1024*1024, "m" },
	gbsuf = { &mbsuf, 1024*1024*1024, "g" },
	stsuf = { NULL, 1, "s" },
	mtsuf = { &stsuf, 60, "m" },
	htsuf = { &mtsuf, 60*60, "h" };
#define	SIZESUF &gbsuf
#define	TIMESUF &htsuf
#define	NOSUF ((Suffix *) NULL)  /* for RLIMIT_NOFILE on SunOS 4.1 */

typedef struct {
	char *name;
	int flag;
	const Suffix *suffix;
} Limit;
static const Limit limits[] = {
	{ "cputime",		RLIMIT_CPU,	TIMESUF },
	{ "filesize",		RLIMIT_FSIZE,	SIZESUF },
	{ "datasize",		RLIMIT_DATA,	SIZESUF },
	{ "stacksize",		RLIMIT_STACK,	SIZESUF },
	{ "coredumpsize",	RLIMIT_CORE,	SIZESUF },
#ifdef RLIMIT_RSS /* SysVr4 does not have this */
	{ "memoryuse",		RLIMIT_RSS,	SIZESUF },
#endif
#ifdef RLIMIT_VMEM /* instead, they have this! */
	{ "vmemory",		RLIMIT_VMEM,	SIZESUF },
#endif
#ifdef RLIMIT_NOFILE  /* SunOS 4.1 adds a limit on file descriptors */
	{ "descriptors",	RLIMIT_NOFILE,	NOSUF },
#endif
	{ NULL, 0, NULL }
};

extern int getrlimit(int, struct rlimit *);
extern int setrlimit(int, const struct rlimit *);

static void printlimit(const Limit *limit, bool hard) {
	struct rlimit rlim;
	long lim;
	getrlimit(limit->flag, &rlim);
	if (hard)
		lim = rlim.rlim_max;
	else
		lim = rlim.rlim_cur;
	if (lim == RLIM_INFINITY)
		fprint(1, "%s \tunlimited\n", limit->name);
	else {
		const Suffix *suf;
		for (suf = limit->suffix; suf != NULL; suf = suf->next)
			if (lim % suf->amount == 0 && (lim != 0 || suf->amount > 1)) {
				lim /= suf->amount;
				break;
			}
		fprint(1, "%s \t%d%s\n", limit->name, lim, (suf == NULL || lim == 0) ? "" : suf->name);
	}
}

static long parselimit(const Limit *limit, char *s) {
	char *t;
	int len = strlen(s);
	long lim = 1;
	const Suffix *suf = limit->suffix;
	if (streq(s, "unlimited"))
		return RLIM_INFINITY;
	if (suf == TIMESUF && (t = strchr(s, ':')) != NULL) {
		*t++ = '\0';
		lim = 60 * a2u(s) + a2u(t);
	} else {
		for (; suf != NULL; suf = suf->next)
			if (streq(suf->name, s + len - strlen(suf->name))) {
				s[len - strlen(suf->name)] = '\0';
				lim *= suf->amount;
				break;
			}
		lim *= a2u(s);
	}
	return lim;
}

void
b_limit ( char ** av ) {
	const Limit *lp = limits;
	bool hard = FALSE;
	if (*++av != NULL && streq(*av, "-h")) {
		av++;
		hard = TRUE;
	}
	if (*av == NULL) {
		for (; lp->name != NULL; lp++)
			printlimit(lp, hard);
		return;
	}
	for (;; lp++) {
		if (lp->name == NULL) {
			fprint(2, "no such limit\n");
			set(FALSE);
			return;
		}
		if (streq(*av, lp->name))
			break;
	}
	if (*++av == NULL)
		printlimit(lp, hard);
	else {
		struct rlimit rlim;
		long pl;
		getrlimit(lp->flag, &rlim);
		if ((pl = parselimit(lp, *av)) < 0) {
			fprint(2, "bad limit\n");
			set(FALSE);
			return;
		}
		if (hard)
			rlim.rlim_max = pl;
		else
			rlim.rlim_cur = pl;
		if (setrlimit(lp->flag, &rlim) == -1) {
			uerror("setrlimit");
			set(FALSE);
		} else
			set(TRUE);
	}
}
