diff --git a/check.t b/check.t
index 92ef550..d02ee38 100644
--- a/check.t
+++ b/check.t
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.186 2008/04/20 01:23:49 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.187 2008/04/20 21:30:28 tg Exp $
 # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@@ -4977,3 +4977,16 @@ expected-stdout:
 	000001A0  BF 0A FF 0A C2 0A EF BF - C0 0A C0 80 0A E0 80 80  |.�.�.���.��.���|
 	000001B0  0A EF BF BD EF BF BE EF - BF BF 0A                 |.�������.|
 ---
+name: ulimit-1
+description:
+	Check if we can use a specific syntax idiom for ulimit
+stdin:
+	if ! x=$(ulimit -d); then
+		print expected to fail on this OS
+	else
+		ulimit -dS $x && print okay
+	fi
+expected-stdout:
+	okay
+---
+
diff --git a/funcs.c b/funcs.c
index fcf04a1..10a550b 100644
--- a/funcs.c
+++ b/funcs.c
@@ -5,7 +5,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.77 2008/04/19 22:15:03 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.78 2008/04/20 21:30:29 tg Exp $");
 
 /* A leading = means assignments before command are kept;
  * a leading * means a POSIX special builtin;
@@ -2860,9 +2860,6 @@ struct limits {
 	char option;
 };
 
-static void print_ulimit(const struct limits *, int);
-static int set_ulimit(const struct limits *, const char *, int);
-
 int
 c_ulimit(const char **wp)
 {
@@ -2909,23 +2906,23 @@ c_ulimit(const char **wp)
 #endif
 		{ NULL, 0, 0, 0 }
 	};
-	static char opts[4 + NELEM(limits) * 2];
-	int how = SOFT | HARD, optc;
-	bool all = false;
+	static char opts[3 + NELEM(limits)];
+	rlim_t val = (rlim_t)0;
+	int how = SOFT | HARD, optc, what;
+	bool all = false, set;
 	const struct limits *l;
+	struct rlimit limit;
 
 	if (!opts[0]) {
 		/* build options string on first call - yuck */
 		char *p = opts;
 
 		*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
-		for (l = limits; l->name; l++) {
+		for (l = limits; l->name; l++)
 			*p++ = l->option;
-			*p++ = '#';
-		}
 		*p = '\0';
 	}
-	/* first check for -a, -H and -S */
+	what = 'f';
 	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
 		switch (optc) {
 		case 'H':
@@ -2940,96 +2937,85 @@ c_ulimit(const char **wp)
 		case '?':
 			return (1);
 		default:
-			break;
+			what = optc;
 		}
 
-	if (wp[builtin_opt.optind] != NULL) {
-		bi_errorf("usage: ulimit [-acdfHLlmnpSsTtvw] [value]");
+	for (l = limits; l->name && l->option != what; l++)
+		;
+	if (!l->name) {
+		internal_warningf("ulimit: %c", what);
 		return (1);
 	}
 
-	/* then parse and act on the actual limits, one at a time */
-	ksh_getopt_reset(&builtin_opt, GF_ERROR);
-	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
-		switch (optc) {
-		case 'a':
-		case 'H':
-		case 'S':
-			break;
-		case '?':
-			return (1);
-		default:
-			for (l = limits; l->name && l->option != optc; l++)
-				;
-			if (!l->name) {
-				internal_warningf("ulimit: %c", optc);
-				return (1);
-			}
-			if (!builtin_opt.optarg)
-				print_ulimit(l, how);
-			else if (set_ulimit(l, builtin_opt.optarg, how))
-				return (1);
-			break;
-		}
-
 	wp += builtin_opt.optind;
+	if ((set = *wp ? true : false)) {
+		if (all || wp[1]) {
+			bi_errorf("too many arguments");
+			return (1);
+		}
+		if (strcmp(wp[0], "unlimited") == 0)
+			val = (rlim_t)RLIM_INFINITY;
+		else {
+			long rval;
 
-	if (all)
+			if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR, false))
+				return (1);
+			/* Avoid problems caused by typos that
+			 * evaluate misses due to evaluating unset
+			 * parameters to 0...
+			 * If this causes problems, will have to
+			 * add parameter to evaluate() to control
+			 * if unset params are 0 or an error.
+			 */
+			if (!rval && !ksh_isdigit(wp[0][0])) {
+				bi_errorf("invalid limit: %s", wp[0]);
+				return (1);
+			}
+			val = (rlim_t)((rlim_t)rval * l->factor);
+		}
+	}
+	if (all) {
 		for (l = limits; l->name; l++) {
 			shprintf("%-20s ", l->name);
-			print_ulimit(l, how);
+			if (getrlimit(l->resource, &limit)) {
+				shf_puts("unknown\n", shl_stdout);
+				continue;
+			}
+			if (how & SOFT)
+				val = limit.rlim_cur;
+			else if (how & HARD)
+				val = limit.rlim_max;
+			if (val == (rlim_t)RLIM_INFINITY)
+				shf_puts("unlimited\n", shl_stdout);
+			else {
+				val = (rlim_t)(val / l->factor);
+				shprintf("%ld\n", (long)val);
+			}
 		}
-	else if (builtin_opt.optind == 1) {
-		/* no limit specified, use file size as default */
-#ifndef RLIMIT_FSIZE
-		internal_warningf("ulimit: need argument");
-		return (1);
-#else
-#ifdef RLIMIT_CPU
-		l = &limits[1];
-#else
-		l = &limits[0];
-#endif
-		if (!wp[0])
-			print_ulimit(l, how);
-		else if (set_ulimit(l, wp[0], how))
-			return(1);
-#endif
+		return 0;
 	}
-	return (0);
-}
-
-static int
-set_ulimit(const struct limits *l, const char *v, int how)
-{
-	rlim_t val = (rlim_t)0;
-	struct rlimit limit;
-
-	if (strcmp(v, "unlimited") == 0)
-		val = (rlim_t)RLIM_INFINITY;
-	else {
-		long rval;
-
-		if (!evaluate(v, &rval, KSH_RETURN_ERROR, false))
-			return (1);
-		/*
-		 * Avoid problems caused by typos that evaluate misses due
-		 * to evaluating unset parameters to 0...
-		 * If this causes problems, will have to add parameter to
-		 * evaluate() to control if unset params are 0 or an error.
-		 */
-		if (!rval && !ksh_isdigit(v[0])) {
-			bi_errorf("invalid limit: %s", v);
-			return (1);
-		}
-		val = (rlim_t)rval * l->factor;
-	}
-
 	if (getrlimit(l->resource, &limit) < 0) {
 		/* some cannot be read, e.g. Linux RLIMIT_LOCKS */
+		if (!set) {
+			shf_puts("unknown\n", shl_stdout);
+			return (0);
+		}
 		limit.rlim_cur = RLIM_INFINITY;
 		limit.rlim_max = RLIM_INFINITY;
 	}
+	if (!set) {
+		if (how & SOFT)
+			val = limit.rlim_cur;
+		else if (how & HARD)
+			val = limit.rlim_max;
+		if (val == (rlim_t)RLIM_INFINITY)
+			shf_puts("unlimited\n", shl_stdout);
+		else {
+			val = (rlim_t)(val / l->factor);
+			shprintf("%ld\n", (long)val);
+		}
+		return (0);
+	}
 	if (how & SOFT)
 		limit.rlim_cur = val;
 	if (how & HARD)
@@ -3044,28 +3030,6 @@ set_ulimit(const struct limits *l, const char *v, int how)
 	return (0);
 }
 
-static void
-print_ulimit(const struct limits *l, int how)
-{
-	rlim_t val = (rlim_t)0;
-	struct rlimit limit;
-
-	if (getrlimit(l->resource, &limit)) {
-		shf_puts("unknown\n", shl_stdout);
-		return;
-	}
-	if (how & SOFT)
-		val = limit.rlim_cur;
-	else if (how & HARD)
-		val = limit.rlim_max;
-	if (val == RLIM_INFINITY)
-		shf_puts("unlimited\n", shl_stdout);
-	else {
-		val /= l->factor;
-		shprintf("%ld\n", (long)val);
-	}
-}
-
 int
 c_rename(const char **wp)
 {
diff --git a/mksh.1 b/mksh.1
index 04098df..eb11c60 100644
--- a/mksh.1
+++ b/mksh.1
@@ -1,4 +1,4 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.122 2008/04/20 01:47:59 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.123 2008/04/20 21:30:29 tg Exp $
 .\" $OpenBSD: ksh.1,v 1.121 2008/03/21 12:51:19 millert Exp $
 .\"-
 .\" Try to make GNU groff and AT&T nroff more compatible
@@ -4092,16 +4092,15 @@ unless they are also given on the same command line.
 .Pp
 .It Xo
 .Ic ulimit
-.Op Fl acdfHLlmnpSsTtvw Op Ar value
-.Ar ...
+.Op Fl acdfHLlmnpSsTtvw
+.Op Ar value
 .Xc
 Display or set process limits.
 If no options are used, the file size limit
 .Pq Fl f
 is assumed.
 .Ar value ,
-if specified, may be either an arithmetic expression starting with a
-number or the word
+if specified, may be either an arithmetic expression or the word
 .Dq unlimited .
 The limits affect the shell and any processes created by the shell after a
 limit is imposed.