about summary refs log tree commit diff
path: root/Src/Builtins
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:38 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:05:38 +0000
commite74702b467171dbdafb56dfe354794a212e020d9 (patch)
treec295b3e9b2e93e2de10331877442615b0f37e779 /Src/Builtins
parentc175751b501a3a4cb40ad4787340a597ea769be4 (diff)
downloadzsh-e74702b467171dbdafb56dfe354794a212e020d9.tar.gz
zsh-e74702b467171dbdafb56dfe354794a212e020d9.tar.xz
zsh-e74702b467171dbdafb56dfe354794a212e020d9.zip
Initial revision
Diffstat (limited to 'Src/Builtins')
-rw-r--r--Src/Builtins/.cvsignore11
-rw-r--r--Src/Builtins/.distfiles5
-rw-r--r--Src/Builtins/.exrc2
-rw-r--r--Src/Builtins/rlimits.awk72
-rw-r--r--Src/Builtins/rlimits.c593
-rw-r--r--Src/Builtins/rlimits.mdd18
-rw-r--r--Src/Builtins/sched.c214
-rw-r--r--Src/Builtins/sched.mdd3
8 files changed, 918 insertions, 0 deletions
diff --git a/Src/Builtins/.cvsignore b/Src/Builtins/.cvsignore
new file mode 100644
index 000000000..ff73d86c4
--- /dev/null
+++ b/Src/Builtins/.cvsignore
@@ -0,0 +1,11 @@
+Makefile
+Makefile.in
+*.pro
+*.o
+*.o.c
+*.so
+*.mdh
+*.mdhi
+*.mdhs
+*.mdh.tmp
+rlimits.h
diff --git a/Src/Builtins/.distfiles b/Src/Builtins/.distfiles
new file mode 100644
index 000000000..cd36388ef
--- /dev/null
+++ b/Src/Builtins/.distfiles
@@ -0,0 +1,5 @@
+DISTFILES_SRC='
+    .cvsignore .distfiles .exrc
+    rlimits.mdd rlimits.c rlimits.awk
+    sched.mdd sched.c
+'
diff --git a/Src/Builtins/.exrc b/Src/Builtins/.exrc
new file mode 100644
index 000000000..91d0b39ef
--- /dev/null
+++ b/Src/Builtins/.exrc
@@ -0,0 +1,2 @@
+set ai
+set sw=4
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk
new file mode 100644
index 000000000..e2500582a
--- /dev/null
+++ b/Src/Builtins/rlimits.awk
@@ -0,0 +1,72 @@
+#
+# rlimits.awk: {g,n}awk script to generate rlimits.h
+#
+# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems
+# Without 0 + hacks some nawks compare numbers as strings
+#
+BEGIN {limidx = 0}
+
+/^[\t ]*(#[\t ]*define[\t _]*RLIMIT_[A-Z]*[\t ]*[0-9][0-9]*|RLIMIT_[A-Z]*,[\t ]*)/ {
+    limindex = index($0, "RLIMIT_")
+    limtail = substr($0, limindex, 80)
+    split(limtail, tmp)
+    limnam = substr(tmp[1], 8, 20)
+    limnum = tmp[2]
+    # in this case I assume GNU libc resourcebits.h
+    if (limnum == "") {
+	limnum = limidx++
+	limindex = index($0, ",")
+	limnam = substr(limnam, 1, limindex-1)
+    }
+    limrev[limnam] = limnum
+    if (lim[limnum] == "") {
+	lim[limnum] = limnam
+	if (limnum ~ /^[0-9]*$/) {
+	    if (limnam == "MEMLOCK") { msg[limnum] = "memorylocked" }
+	    if (limnam == "RSS")     { msg[limnum] = "resident" }
+	    if (limnam == "VMEM")    { msg[limnum] = "vmemorysize" }
+	    if (limnam == "NOFILE")  { msg[limnum] = "descriptors" }
+	    if (limnam == "OFILE")   { msg[limnum] = "descriptors" }
+	    if (limnam == "CORE")    { msg[limnum] = "coredumpsize" }
+	    if (limnam == "STACK")   { msg[limnum] = "stacksize" }
+	    if (limnam == "DATA")    { msg[limnum] = "datasize" }
+	    if (limnam == "FSIZE")   { msg[limnum] = "filesize" }
+	    if (limnam == "CPU")     { msg[limnum] = "cputime" }
+	    if (limnam == "NPROC")   { msg[limnum] = "maxproc" }
+	    if (limnam == "AS")      { msg[limnum] = "addressspace" }
+	    if (limnam == "TCACHE")  { msg[limnum] = "cachedthreads" }
+        }
+    }
+}
+/^[\t ]*#[\t ]*define[\t _]*RLIM_NLIMITS[\t ]*[0-9][0-9]*/ {
+    limindex = index($0, "RLIM_")
+    limtail = substr($0, limindex, 80)
+    split(limtail, tmp)
+    nlimits = tmp[2]
+}
+# in case of GNU libc
+/^[\t ]*RLIM_NLIMITS[\t ]*=[\t ]*RLIMIT_NLIMITS/ {
+    nlimits = limidx
+}
+
+END {
+    if (limrev["MEMLOCK"] != "") {
+        irss = limrev["RSS"]
+        msg[irss] = "memoryuse"
+    }
+    ps = "%s"
+
+    printf("%s\n%s\n\n", "/** rlimits.h                              **/", "/** architecture-customized limits for zsh **/")
+    printf("#define ZSH_NLIMITS %d\n\nstatic char *recs[ZSH_NLIMITS+1] = {\n", 0 + nlimits)
+
+    for (i = 0; i < 0 + nlimits; i++)
+	if (msg[i] == "") {
+            badlimit++
+            printf("\t%c%s%c,\n", 34, lim[i], 34)
+	} else
+	    printf("\t%c%s%c,\n", 34, msg[i], 34)
+    print "\tNULL"
+    print "};"
+    print ""
+    exit(badlimit)
+}
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
new file mode 100644
index 000000000..20b8d663d
--- /dev/null
+++ b/Src/Builtins/rlimits.c
@@ -0,0 +1,593 @@
+/*
+ * rlimits.c - resource limit builtins
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "rlimits.mdh"
+#include "rlimits.pro"
+
+#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY)
+
+/* Generated rec array containing limits required for the limit builtin.     *
+ * They must appear in this array in numerical order of the RLIMIT_* macros. */
+
+# include "rlimits.h"
+
+# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED)
+static rlim_t
+zstrtorlimt(const char *s, char **t, int base)
+{
+    rlim_t ret = 0;
+ 
+    if (!base)
+	if (*s != '0')
+	    base = 10;
+	else if (*++s == 'x' || *s == 'X')
+	    base = 16, s++;
+	else
+	    base = 8;
+ 
+    if (base <= 10)
+	for (; *s >= '0' && *s < ('0' + base); s++)
+	    ret = ret * base + *s - '0';
+    else
+	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
+	     || (*s >= 'A' && *s < ('A' + base - 10)); s++)
+	    ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
+    if (t)
+	*t = (char *)s;
+    return ret;
+}
+# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+#  define zstrtorlimt(a, b, c)	zstrtol((a), (b), (c))
+# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+
+/* Display resource limits.  hard indicates whether `hard' or `soft'  *
+ * limits should be displayed.  lim specifies the limit, or may be -1 *
+ * to show all.                                                       */
+
+/**/
+static void
+showlimits(int hard, int lim)
+{
+    int rt;
+    rlim_t val;
+
+    /* main loop over resource types */
+    for (rt = 0; rt != ZSH_NLIMITS; rt++)
+	if (rt == lim || lim == -1) {
+	    /* display limit for resource number rt */
+	    printf("%-16s", recs[rt]);
+	    val = (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur;
+	    if (val == RLIM_INFINITY)
+		printf("unlimited\n");
+	    else if (rt==RLIMIT_CPU)
+		/* time-type resource -- display as hours, minutes and
+		seconds. */
+		printf("%d:%02d:%02d\n", (int)(val / 3600),
+		       (int)(val / 60) % 60, (int)(val % 60));
+# ifdef RLIMIT_NPROC
+	    else if (rt == RLIMIT_NPROC)
+		/* pure numeric resource */
+		printf("%d\n", (int)val);
+# endif /* RLIMIT_NPROC */
+# ifdef RLIMIT_NOFILE
+	    else if (rt == RLIMIT_NOFILE)
+		/* pure numeric resource */
+		printf("%d\n", (int)val);
+# endif /* RLIMIT_NOFILE */
+	    else if (val >= 1024L * 1024L)
+		/* memory resource -- display with `K' or `M' modifier */
+# ifdef RLIM_T_IS_QUAD_T
+		printf("%qdMB\n", val / (1024L * 1024L));
+	    else
+		printf("%qdkB\n", val / 1024L);
+# else
+		printf("%ldMB\n", val / (1024L * 1024L));
+            else
+		printf("%ldkB\n", val / 1024L);
+# endif /* RLIM_T_IS_QUAD_T */
+	}
+}
+
+/* Display a resource limit, in ulimit style.  lim specifies which   *
+ * limit should be displayed, and hard indicates whether the hard or *
+ * soft limit should be displayed.                                   */
+
+/**/
+static void
+printulimit(int lim, int hard, int head)
+{
+    rlim_t limit;
+
+    /* get the limit in question */
+    limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
+    /* display the appropriate heading */
+    switch (lim) {
+    case RLIMIT_CPU:
+	if (head)
+	    printf("cpu time (seconds)         ");
+	break;
+    case RLIMIT_FSIZE:
+	if (head)
+	    printf("file size (blocks)         ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 512;
+	break;
+    case RLIMIT_DATA:
+	if (head)
+	    printf("data seg size (kbytes)     ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+    case RLIMIT_STACK:
+	if (head)
+	    printf("stack size (kbytes)        ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+    case RLIMIT_CORE:
+	if (head)
+	    printf("core file size (blocks)    ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 512;
+	break;
+# ifdef RLIMIT_RSS
+    case RLIMIT_RSS:
+	if (head)
+	    printf("resident set size (kbytes) ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+# endif /* RLIMIT_RSS */
+# ifdef RLIMIT_MEMLOCK
+    case RLIMIT_MEMLOCK:
+	if (head)
+	    printf("locked-in-memory size (kb) ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+# endif /* RLIMIT_MEMLOCK */
+# ifdef RLIMIT_NPROC
+    case RLIMIT_NPROC:
+	if (head)
+	    printf("processes                  ");
+	break;
+# endif /* RLIMIT_NPROC */
+# ifdef RLIMIT_NOFILE
+    case RLIMIT_NOFILE:
+	if (head)
+	    printf("file descriptors           ");
+	break;
+# endif /* RLIMIT_NOFILE */
+# ifdef RLIMIT_VMEM
+    case RLIMIT_VMEM:
+	if (head)
+	    printf("virtual memory size (kb)   ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+# endif /* RLIMIT_VMEM */
+# if defined RLIMIT_AS && RLIMIT_AS != RLIMIT_VMEM
+    case RLIMIT_AS:
+	if (head)
+	    printf("address space (kb)         ");
+	if (limit != RLIM_INFINITY)
+	    limit /= 1024;
+	break;
+# endif /* RLIMIT_AS */
+# ifdef RLIMIT_TCACHE
+    case RLIMIT_TCACHE:
+	if (head)
+	    printf("cached threads             ");
+	break;
+# endif /* RLIMIT_TCACHE */
+    }
+    /* display the limit */
+    if (limit == RLIM_INFINITY)
+	printf("unlimited\n");
+    else
+	printf("%ld\n", (long)limit);
+}
+
+/* limit: set or show resource limits.  The variable hard indicates *
+ * whether `hard' or `soft' resource limits are being set/shown.    */
+
+/**/
+static int
+bin_limit(char *nam, char **argv, char *ops, int func)
+{
+    char *s;
+    int hard, limnum, lim;
+    rlim_t val;
+    int ret = 0;
+
+    hard = ops['h'];
+    if (ops['s'] && !*argv)
+	return setlimits(NULL);
+    /* without arguments, display limits */
+    if (!*argv) {
+	showlimits(hard, -1);
+	return 0;
+    }
+    while ((s = *argv++)) {
+	/* Search for the appropriate resource name.  When a name matches (i.e. *
+	 * starts with) the argument, the lim variable changes from -1 to the   *
+	 * number of the resource.  If another match is found, lim goes to -2.  */
+	for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
+	    if (!strncmp(recs[limnum], s, strlen(s))) {
+		if (lim != -1)
+		    lim = -2;
+		else
+		    lim = limnum;
+	    }
+	/* lim==-1 indicates that no matches were found.       *
+	 * lim==-2 indicates that multiple matches were found. */
+	if (lim < 0) {
+	    zwarnnam("limit",
+		     (lim == -2) ? "ambiguous resource specification: %s"
+		     : "no such resource: %s", s, 0);
+	    return 1;
+	}
+	/* without value for limit, display the current limit */
+	if (!(s = *argv++)) {
+	    showlimits(hard, lim);
+	    return 0;
+	}
+	if (lim==RLIMIT_CPU) {
+	    /* time-type resource -- may be specified as seconds, or minutes or *
+	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
+	     * together more than one of these.  It's easier to understand from *
+	     * the code:                                                        */
+	    val = zstrtorlimt(s, &s, 10);
+	    if (*s)
+		if ((*s == 'h' || *s == 'H') && !s[1])
+		    val *= 3600L;
+		else if ((*s == 'm' || *s == 'M') && !s[1])
+		    val *= 60L;
+		else if (*s == ':')
+		    val = val * 60 + zstrtorlimt(s + 1, &s, 10);
+		else {
+		    zwarnnam("limit", "unknown scaling factor: %s", s, 0);
+		    return 1;
+		}
+	}
+# ifdef RLIMIT_NPROC
+	else if (lim == RLIMIT_NPROC)
+	    /* pure numeric resource -- only a straight decimal number is
+	    permitted. */
+	    val = zstrtorlimt(s, &s, 10);
+# endif /* RLIMIT_NPROC */
+# ifdef RLIMIT_NOFILE
+	else if (lim == RLIMIT_NOFILE)
+	    /* pure numeric resource -- only a straight decimal number is
+	    permitted. */
+	    val = zstrtorlimt(s, &s, 10);
+# endif /* RLIMIT_NOFILE */
+	else {
+	    /* memory-type resource -- `k' and `M' modifiers are permitted,
+	    meaning (respectively) 2^10 and 2^20. */
+	    val = zstrtorlimt(s, &s, 10);
+	    if (!*s || ((*s == 'k' || *s == 'K') && !s[1]))
+		val *= 1024L;
+	    else if ((*s == 'M' || *s == 'm') && !s[1])
+		val *= 1024L * 1024;
+	    else {
+		zwarnnam("limit", "unknown scaling factor: %s", s, 0);
+		return 1;
+	    }
+	}
+	/* new limit is valid and has been interpreted; apply it to the
+	specified resource */
+	if (hard) {
+	    /* can only raise hard limits if running as root */
+	    if (val > current_limits[lim].rlim_max && geteuid()) {
+		zwarnnam("limit", "can't raise hard limits", NULL, 0);
+		return 1;
+	    } else {
+		limits[lim].rlim_max = val;
+		if (val < limits[lim].rlim_cur)
+		    limits[lim].rlim_cur = val;
+	    }
+	} else if (val > limits[lim].rlim_max) {
+	    zwarnnam("limit", "limit exceeds hard limit", NULL, 0);
+	    return 1;
+	} else
+	    limits[lim].rlim_cur = val;
+	if (ops['s'] && zsetlimit(lim, "limit"))
+	    ret++;
+    }
+    return ret;
+}
+
+/* unlimit: remove resource limits.  Much of this code is the same as *
+ * that in bin_limit().                                               */
+
+/**/
+static int
+bin_unlimit(char *nam, char **argv, char *ops, int func)
+{
+    int hard, limnum, lim;
+    int ret = 0;
+    uid_t euid = geteuid();
+
+    hard = ops['h'];
+    /* Without arguments, remove all limits. */
+    if (!*argv) {
+	for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) {
+	    if (hard)
+		if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY)
+		    ret++;
+		else
+		    limits[limnum].rlim_max = RLIM_INFINITY;
+	    else
+		limits[limnum].rlim_cur = limits[limnum].rlim_max;
+	}
+	if (ops['s'])
+	    ret += setlimits(nam);
+	if (ret)
+	    zwarnnam(nam, "can't remove hard limits", NULL, 0);
+    } else {
+	for (; *argv; argv++) {
+	    /* Search for the appropriate resource name.  When a name     *
+	     * matches (i.e. starts with) the argument, the lim variable  *
+	     * changes from -1 to the number of the resource.  If another *
+	     * match is found, lim goes to -2.                            */
+	    for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
+		if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
+		    if (lim != -1)
+			lim = -2;
+		    else
+			lim = limnum;
+		}
+	    /* lim==-1 indicates that no matches were found.       *
+	     * lim==-2 indicates that multiple matches were found. */
+	    if (lim < 0) {
+		zwarnnam(nam,
+			 (lim == -2) ? "ambiguous resource specification: %s"
+			 : "no such resource: %s", *argv, 0);
+		return 1;
+	    }
+	    /* remove specified limit */
+	    if (hard)
+		if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
+		    zwarnnam(nam, "can't remove hard limits", NULL, 0);
+		    ret++;
+		} else
+		    limits[lim].rlim_max = RLIM_INFINITY;
+	    else
+		limits[lim].rlim_cur = limits[lim].rlim_max;
+	    if (ops['s'] && zsetlimit(lim, nam))
+		ret++;
+	}
+    }
+    return ret;
+}
+
+/* ulimit: set or display resource limits */
+
+/**/
+static int
+bin_ulimit(char *name, char **argv, char *ops, int func)
+{
+    int res, resmask = 0, hard = 0, soft = 0, nres = 0;
+    char *options;
+
+    do {
+	options = *argv;
+	if (options && *options == '-' && !options[1]) {
+	    zwarnnam(name, "missing option letter", NULL, 0);
+	    return 1;
+	}
+	res = -1;
+	if (options && *options == '-') {
+	    argv++;
+	    while (*++options) {
+		if(*options == Meta)
+		    *++options ^= 32;
+		res = -1;
+		switch (*options) {
+		case 'H':
+		    hard = 1;
+		    continue;
+		case 'S':
+		    soft = 1;
+		    continue;
+		case 'a':
+		    if (*argv || options[1] || resmask) {
+			zwarnnam(name, "no arguments required after -a",
+				 NULL, 0);
+			return 1;
+		    }
+		    resmask = (1 << RLIM_NLIMITS) - 1;
+		    nres = RLIM_NLIMITS;
+		    continue;
+		case 't':
+		    res = RLIMIT_CPU;
+		    break;
+		case 'f':
+		    res = RLIMIT_FSIZE;
+		    break;
+		case 'd':
+		    res = RLIMIT_DATA;
+		    break;
+		case 's':
+		    res = RLIMIT_STACK;
+		    break;
+		case 'c':
+		    res = RLIMIT_CORE;
+		    break;
+# ifdef RLIMIT_RSS
+		case 'm':
+		    res = RLIMIT_RSS;
+		    break;
+# endif /* RLIMIT_RSS */
+# ifdef RLIMIT_MEMLOCK
+		case 'l':
+		    res = RLIMIT_MEMLOCK;
+		    break;
+# endif /* RLIMIT_MEMLOCK */
+# ifdef RLIMIT_NOFILE
+		case 'n':
+		    res = RLIMIT_NOFILE;
+		    break;
+# endif /* RLIMIT_NOFILE */
+# ifdef RLIMIT_NPROC
+		case 'u':
+		    res = RLIMIT_NPROC;
+		    break;
+# endif /* RLIMIT_NPROC */
+# ifdef RLIMIT_VMEM
+		case 'v':
+		    res = RLIMIT_VMEM;
+		    break;
+# endif /* RLIMIT_VMEM */
+		default:
+		    /* unrecognised limit */
+		    zwarnnam(name, "bad option: -%c", NULL, *options);
+		    return 1;
+		}
+		if (options[1]) {
+		    resmask |= 1 << res;
+		    nres++;
+		}
+	    }
+	}
+	if (!*argv || **argv == '-') {
+	    if (res < 0)
+		if (*argv || nres)
+		    continue;
+		else
+		    res = RLIMIT_FSIZE;
+	    resmask |= 1 << res;
+	    nres++;
+	    continue;
+	}
+	if (res < 0)
+	    res = RLIMIT_FSIZE;
+	if (strcmp(*argv, "unlimited")) {
+	    /* set limit to specified value */
+	    rlim_t limit;
+
+	    limit = zstrtorlimt(*argv, NULL, 10);
+	    /* scale appropriately */
+	    switch (res) {
+	    case RLIMIT_FSIZE:
+	    case RLIMIT_CORE:
+		limit *= 512;
+		break;
+	    case RLIMIT_DATA:
+	    case RLIMIT_STACK:
+# ifdef RLIMIT_RSS
+	    case RLIMIT_RSS:
+# endif /* RLIMIT_RSS */
+# ifdef RLIMIT_MEMLOCK
+	    case RLIMIT_MEMLOCK:
+# endif /* RLIMIT_MEMLOCK */
+# ifdef RLIMIT_VMEM
+	    case RLIMIT_VMEM:
+# endif /* RLIMIT_VMEM */
+		limit *= 1024;
+		break;
+	    }
+	    if (hard) {
+		/* can't raise hard limit unless running as root */
+		if (limit > current_limits[res].rlim_max && geteuid()) {
+		    zwarnnam(name, "can't raise hard limits", NULL, 0);
+		    return 1;
+		}
+		limits[res].rlim_max = limit;
+		if (limit < limits[res].rlim_cur)
+		    limits[res].rlim_cur = limit;
+	    }
+	    if (!hard || soft) {
+		/* can't raise soft limit above hard limit */
+		if (limit > limits[res].rlim_max) {
+		    if (limit > current_limits[res].rlim_max && geteuid()) {
+			zwarnnam(name, "value exceeds hard limit", NULL, 0);
+			return 1;
+		    }
+		    limits[res].rlim_max = limits[res].rlim_cur = limit;
+		} else
+		    limits[res].rlim_cur = limit;
+	    }
+	} else {
+	    /* remove specified limit */
+	    if (hard) {
+		/* can't remove hard limit unless running as root */
+		if (current_limits[res].rlim_max != RLIM_INFINITY && geteuid()) {
+		    zwarnnam(name, "can't remove hard limits", NULL, 0);
+		    return 1;
+		}
+		limits[res].rlim_max = RLIM_INFINITY;
+	    }
+	    if (!hard || soft)
+		/* `removal' of soft limit means setting it equal to the
+		   corresponding hard limit */
+		limits[res].rlim_cur = limits[res].rlim_max;
+	}
+	if (zsetlimit(res, name))
+	    return 1;
+	argv++;
+    } while (*argv);
+    for (res = 0; res < RLIM_NLIMITS; res++, resmask >>= 1)
+	if (resmask & 1)
+	    printulimit(res, hard, nres > 1);
+    return 0;
+}
+
+#else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */
+
+# define bin_limit   bin_notavail
+# define bin_ulimit  bin_notavail
+# define bin_unlimit bin_notavail
+
+#endif /* !HAVE_GETRLIMIT || !RLIM_INFINITY */
+
+static struct builtin bintab[] = {
+    BUILTIN("limit",   0, bin_limit,   0, -1, 0, "sh", NULL),
+    BUILTIN("ulimit",  0, bin_ulimit,  0, -1, 0, NULL, NULL),
+    BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL),
+};
+
+/**/
+int
+boot_rlimits(Module m)
+{
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_rlimits(Module m)
+{
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+#endif
diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd
new file mode 100644
index 000000000..f0e41b73e
--- /dev/null
+++ b/Src/Builtins/rlimits.mdd
@@ -0,0 +1,18 @@
+autobins="limit ulimit unlimit"
+
+objects="rlimits.o"
+
+:<<\Make
+rlimits.o rlimits..o: rlimits.h
+
+# this file will not be made if limits are unavailable:
+# silent so the warning doesn't appear unless necessary
+rlimits.h: rlimits.awk @RLIMITS_INC_H@
+	@echo '$(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ > rlimits.h'; \
+	$(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ > rlimits.h || \
+	    echo WARNING: unknown limits:  mail rlimits.h to developers
+
+clean-here: clean.rlimits
+clean.rlimits:
+	rm -f rlimits.h
+Make
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
new file mode 100644
index 000000000..b4914899e
--- /dev/null
+++ b/Src/Builtins/sched.c
@@ -0,0 +1,214 @@
+/*
+ * sched.c - execute commands at scheduled times
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "sched.mdh"
+#include "sched.pro"
+
+/* node in sched list */
+
+typedef struct schedcmd  *Schedcmd;
+
+struct schedcmd {
+    struct schedcmd *next;
+    char *cmd;			/* command to run */
+    time_t time;		/* when to run it */
+};
+
+/* the list of sched jobs pending */
+ 
+static struct schedcmd *schedcmds;
+
+/**/
+static int
+bin_sched(char *nam, char **argv, char *ops, int func)
+{
+    char *s = *argv++;
+    time_t t;
+    long h, m;
+    struct tm *tm;
+    struct schedcmd *sch, *sch2, *schl;
+    int sn;
+
+    /* If the argument begins with a -, remove the specified item from the
+    schedule. */
+    if (s && *s == '-') {
+	sn = atoi(s + 1);
+
+	if (!sn) {
+	    zwarnnam("sched", "usage for delete: sched -<item#>.", NULL, 0);
+	    return 1;
+	}
+	for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds, sn--;
+	     sch && sn; sch = (schl = sch)->next, sn--);
+	if (!sch) {
+	    zwarnnam("sched", "not that many entries", NULL, 0);
+	    return 1;
+	}
+	schl->next = sch->next;
+	zsfree(sch->cmd);
+	zfree(sch, sizeof(struct schedcmd));
+
+	return 0;
+    }
+
+    /* given no arguments, display the schedule list */
+    if (!s) {
+	char tbuf[40];
+
+	for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
+	    t = sch->time;
+	    tm = localtime(&t);
+	    ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm);
+	    printf("%3d %s %s\n", sn, tbuf, sch->cmd);
+	}
+	return 0;
+    } else if (!*argv) {
+	/* other than the two cases above, sched *
+	 *requires at least two arguments        */
+	zwarnnam("sched", "not enough arguments", NULL, 0);
+	return 1;
+    }
+
+    /* The first argument specifies the time to schedule the command for.  The
+    remaining arguments form the command. */
+    if (*s == '+') {
+	/* + introduces a relative time.  The rest of the argument is an
+	hour:minute offset from the current time.  Once the hour and minute
+	numbers have been extracted, and the format verified, the resulting
+	offset is simply added to the current time. */
+	h = zstrtol(s + 1, &s, 10);
+	if (*s != ':') {
+	    zwarnnam("sched", "bad time specifier", NULL, 0);
+	    return 1;
+	}
+	m = zstrtol(s + 1, &s, 10);
+	if (*s) {
+	    zwarnnam("sched", "bad time specifier", NULL, 0);
+	    return 1;
+	}
+	t = time(NULL) + h * 3600 + m * 60;
+    } else {
+	/* If there is no +, an absolute time of day must have been given.
+	This is in hour:minute format, optionally followed by a string starting
+	with `a' or `p' (for a.m. or p.m.).  Characters after the `a' or `p'
+	are ignored. */
+	h = zstrtol(s, &s, 10);
+	if (*s != ':') {
+	    zwarnnam("sched", "bad time specifier", NULL, 0);
+	    return 1;
+	}
+	m = zstrtol(s + 1, &s, 10);
+	if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
+	    zwarnnam("sched", "bad time specifier", NULL, 0);
+	    return 1;
+	}
+	t = time(NULL);
+	tm = localtime(&t);
+	t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
+	if (*s == 'p' || *s == 'P')
+	    h += 12;
+	t += h * 3600 + m * 60;
+	/* If the specified time is before the current time, it must refer to
+	tomorrow. */
+	if (t < time(NULL))
+	    t += 3600 * 24;
+    }
+    /* The time has been calculated; now add the new entry to the linked list
+    of scheduled commands. */
+    sch = (struct schedcmd *) zcalloc(sizeof *sch);
+    sch->time = t;
+    PERMALLOC {
+	sch->cmd = zjoin(argv, ' ');
+    } LASTALLOC;
+    sch->next = NULL;
+    for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next);
+    sch2->next = sch;
+    return 0;
+}
+
+/* Check scheduled commands; call this function from time to time. */
+
+/**/
+static void
+checksched(void)
+{
+    time_t t;
+    struct schedcmd *sch, *schl;
+
+    if(!schedcmds)
+	return;
+    t = time(NULL);
+    for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds; sch;
+	 sch = (schl = sch)->next) {
+	if (sch->time <= t) {
+	    execstring(sch->cmd, 0, 0);
+	    schl->next = sch->next;
+	    zsfree(sch->cmd);
+	    zfree(sch, sizeof(struct schedcmd));
+	    sch = schl;
+	}
+    }
+}
+
+static void (*p_checksched) _((void)) = checksched;
+static struct linknode n_checksched = { NULL, NULL, &p_checksched };
+
+static struct builtin bintab[] = {
+    BUILTIN("sched", 0, bin_sched, 0, -1, 0, NULL, NULL),
+};
+
+/**/
+int
+boot_sched(Module m)
+{
+    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+	return 1;
+    uaddlinknode(prepromptfns, &n_checksched);
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_sched(Module m)
+{
+    struct schedcmd *sch, *schn;
+
+    for (sch = schedcmds; sch; sch = schn) {
+	schn = sch->next;
+	zsfree(sch->cmd);
+	zfree(sch, sizeof(*sch));
+    }
+    uremnode(prepromptfns, &n_checksched);
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+#endif
diff --git a/Src/Builtins/sched.mdd b/Src/Builtins/sched.mdd
new file mode 100644
index 000000000..6ed749f32
--- /dev/null
+++ b/Src/Builtins/sched.mdd
@@ -0,0 +1,3 @@
+autobins="sched"
+
+objects="sched.o"