xref: /netbsd-src/bin/sh/miscbltin.c (revision 6b10aaf380eeec923ba96d5e99a436eb57fff1bb)
1*6b10aaf3Skre /*	$NetBSD: miscbltin.c,v 1.56 2024/10/12 23:34:56 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*6b10aaf3Skre __RCSID("$NetBSD: miscbltin.c,v 1.56 2024/10/12 23:34:56 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
4461f28255Scgd /*
45b5a5957fSandvar  * Miscellaneous builtins.
4661f28255Scgd  */
4761f28255Scgd 
48e3f5fb92Schristos #include <sys/param.h>		/* BSD4_4 */
494899054aSkre #include <sys/resource.h>
50759eadefSjtc #include <sys/stat.h>
5107bae7edSchristos #include <sys/time.h>
524899054aSkre #include <sys/types.h>		/* quad_t */
534899054aSkre 
54a81e4124Sjtc #include <ctype.h>
551d9dab3eSchristos #include <errno.h>
564899054aSkre #include <limits.h>
574899054aSkre #include <stdlib.h>
584899054aSkre #ifndef SMALL
594899054aSkre #include <termios.h>
604899054aSkre #endif
614899054aSkre #include <unistd.h>
6207bae7edSchristos 
6361f28255Scgd #include "shell.h"
6461f28255Scgd #include "options.h"
6561f28255Scgd #include "var.h"
664899054aSkre #include "input.h"		/* for whichprompt */
6761f28255Scgd #include "output.h"
684899054aSkre #include "parser.h"		/* for getprompt() */
6961f28255Scgd #include "memalloc.h"
7061f28255Scgd #include "error.h"
714fc4fe2eSchristos #include "builtins.h"
7261f28255Scgd #include "mystring.h"
73c15b76c2Skre #include "redir.h"		/* for user_fd_limit */
7461f28255Scgd 
7561f28255Scgd /*
764803d37fSdsl  * The read builtin.
77b5a5957fSandvar  * Backslashes escape the next char unless -r is specified.
7861f28255Scgd  *
7961f28255Scgd  * This uses unbuffered input, which may be avoidable in some cases.
804803d37fSdsl  *
814803d37fSdsl  * Note that if IFS=' :' then read x y should work so that:
824803d37fSdsl  * 'a b'	x='a', y='b'
834803d37fSdsl  * ' a b '	x='a', y='b'
844803d37fSdsl  * ':b'		x='',  y='b'
854803d37fSdsl  * ':'		x='',  y=''
864803d37fSdsl  * '::'		x='',  y=''
874803d37fSdsl  * ': :'	x='',  y=''
884803d37fSdsl  * ':::'	x='',  y='::'
894803d37fSdsl  * ':b c:'	x='',  y='b c:'
9061f28255Scgd  */
9161f28255Scgd 
924899054aSkre #ifndef SMALL
934899054aSkre static int b_flag;
944899054aSkre 
954899054aSkre static int
964899054aSkre setrawmode(int fd, int on, int end, struct termios *t)
974899054aSkre {
984899054aSkre 	struct termios n;
994899054aSkre 
1004899054aSkre 	if (on) {
1014899054aSkre 		if (tcgetattr(fd, t) != 0)
1024899054aSkre 			return 0;
1034899054aSkre 		n = *t;
1044899054aSkre 		if (on == 1 && b_flag) {
1054899054aSkre 			n.c_cc[VEOL] = end;
1064899054aSkre 		} else {
1074899054aSkre 			cfmakeraw(&n);
1084899054aSkre 			n.c_iflag |= ICRNL;
1094899054aSkre 			n.c_oflag = t->c_oflag;
1104899054aSkre 			n.c_lflag |= ECHO | ISIG;
1114899054aSkre 		}
1124899054aSkre 		if (tcsetattr(fd, TCSADRAIN | TCSASOFT, &n) == 0)
1134899054aSkre 			return 1;
1144899054aSkre 	} else
1154899054aSkre 		(void)tcsetattr(fd, TCSADRAIN | TCSASOFT, t);
1164899054aSkre 	return 0;
1174899054aSkre }
1184899054aSkre 
1194899054aSkre static int
1204899054aSkre is_a_pipe(int fd)
1214899054aSkre {
1224899054aSkre 	if (lseek(fd, 0, SEEK_CUR) == -1 && errno == ESPIPE) {
1234899054aSkre 		errno = 0;
1244899054aSkre 		return 1;
1254899054aSkre 	}
1264899054aSkre 	return 0;
1274899054aSkre }
1284899054aSkre 
1294899054aSkre #define	READ_BUFFER_SIZE	512
1304899054aSkre 
1314899054aSkre static int
1324899054aSkre next_read_char(int fd, size_t max)
1334899054aSkre {
1344899054aSkre 	static char buffer[READ_BUFFER_SIZE];
1354899054aSkre 	static int pos = 0, len = 0;
1364899054aSkre 
1374899054aSkre 	if (max == 0) {
1384899054aSkre 		pos = len = 0;
1394899054aSkre 		return -1;
1404899054aSkre 	}
1414899054aSkre 	if (max == (size_t)-1) {
1424899054aSkre 		/*
1434899054aSkre 		 * If possible, and necessary, rewind the file
1444899054aSkre 		 * so unprocessed data  can be read again next time
1454899054aSkre 		 *
1464899054aSkre 		 * If that fails, never mind (-b allows that to happen)
1474899054aSkre 		 */
1484899054aSkre 		if (b_flag && pos < len)
1494899054aSkre 			(void)lseek(fd, (off_t)(pos - len), SEEK_CUR);
1504899054aSkre 		return -1;
1514899054aSkre 	}
1524899054aSkre 
1534899054aSkre 	if (b_flag == 0) {
1544899054aSkre 		char c;
1554899054aSkre 
1564899054aSkre 		(void) max;
1574899054aSkre 		if (read(fd, &c, 1) != 1)
1584899054aSkre 			return -1;
1594899054aSkre 		return (c & 0xFF);
1604899054aSkre 	}
1614899054aSkre 
1624899054aSkre 	if (pos >= len) {
1634899054aSkre 		pos = 0;
1644899054aSkre 		if (max > sizeof buffer)
1654899054aSkre 			max = sizeof buffer;
1664899054aSkre 		len = read(fd, buffer, max);
1674899054aSkre 		if (len <= 0)
1684899054aSkre 			return -1;
1694899054aSkre 	}
1704899054aSkre 
1714899054aSkre 	return buffer[pos++] & 0xFF;
1724899054aSkre }
1734899054aSkre 
1744899054aSkre #define READ_OPTS	"bd:n:p:r"
1754899054aSkre 
1764899054aSkre #else
1774899054aSkre 
1784899054aSkre static inline int
1794899054aSkre next_read_char(int fd, size_t max)
1804899054aSkre {
1814899054aSkre 	char c;
1824899054aSkre 
1834899054aSkre 	if (max == 0 || max == (size_t)-1)
1844899054aSkre 		return 0;
1854899054aSkre 
1864899054aSkre 	if (read(fd, &c, 1) != 1)
1874899054aSkre 		return -1;
1884899054aSkre 	return (c & 0xFF);
1894899054aSkre }
1904899054aSkre 
1914899054aSkre #define n_flag 0
1924899054aSkre #define maxlen 0
1934899054aSkre 
1944899054aSkre #define READ_OPTS	"d:p:r"
1954899054aSkre 
1964899054aSkre #endif
1974899054aSkre 
1984ce0d34aScgd int
199c02b3bbdSchristos readcmd(int argc, char **argv)
2004ce0d34aScgd {
20161f28255Scgd 	char **ap;
2024899054aSkre 	int c;
203e2731928Skre 	char end;
2044899054aSkre 	int r_flag;
20561f28255Scgd 	char *prompt;
2064803d37fSdsl 	const char *ifs;
20761f28255Scgd 	char *p;
20861f28255Scgd 	int startword;
20961f28255Scgd 	int status;
21061f28255Scgd 	int i;
2114803d37fSdsl 	int is_ifs;
2124803d37fSdsl 	int saveall = 0;
2134899054aSkre 	int read_tty = 0;
214143e8d55Skre 	ptrdiff_t wordlen = 0;
21528b2f4e3Skre 	char *newifs = NULL;
21628b2f4e3Skre 	struct stackmark mk;
21761f28255Scgd 
2184899054aSkre #ifndef SMALL
2194899054aSkre 	struct termios ttystate;
2204899054aSkre 	int n_flag, maxlen;
2214899054aSkre 	int setraw = 0;
2224899054aSkre 
2234899054aSkre 	b_flag = 0;
2244899054aSkre 	n_flag = 0;
2254899054aSkre 	maxlen = READ_BUFFER_SIZE - 1;
2264899054aSkre #endif
2274899054aSkre 
228e2731928Skre 	end = '\n';				/* record delimiter */
2294899054aSkre 	r_flag = 0;
23061f28255Scgd 	prompt = NULL;
2314899054aSkre 	whichprompt = 2;			/* for continuation lines */
2324899054aSkre 
2334899054aSkre 	while ((i = nextopt(READ_OPTS)) != '\0') {
234e2731928Skre 		switch (i) {
235e2731928Skre 		case 'd':
236e2731928Skre 			end = *optionarg;	/* even if '\0' */
237e2731928Skre 			break;
238e2731928Skre 		case 'p':
23933809804Schristos 			prompt = optionarg;
240e2731928Skre 			break;
241e2731928Skre 		case 'r':
2424899054aSkre 			r_flag = 1;
243e2731928Skre 			break;
2444899054aSkre #ifndef SMALL
2454899054aSkre 		case 'n':
2464899054aSkre 			maxlen = number(optionarg);
2474899054aSkre 			if (maxlen > (INT_MAX >> 8) + 1)	/* sanity */
2484899054aSkre 				error("-n %s too large", optionarg);
2494899054aSkre 			n_flag = 1;
2504899054aSkre 			break;
2514899054aSkre 		case 'b':
2524899054aSkre 			if (!is_a_pipe(0))
2534899054aSkre 				b_flag = 1;
2544899054aSkre 			break;
2554899054aSkre #endif
256e2731928Skre 		}
25761f28255Scgd 	}
2584803d37fSdsl 
259a1f66056Skre 	if (*(ap = argptr) == NULL)
260a1f66056Skre 		error("variable name required\n"
2614899054aSkre #ifdef SMALL
2624899054aSkre 		      "Usage: read [-r] [-d C] [-p prompt] var...");
2634899054aSkre #else
2644899054aSkre 		      "Usage: read [-br] [-d C] [-n len] [-p prompt] var...");
265a1f66056Skre 
2664899054aSkre 	(void)next_read_char(0, 0);	/* make sure the buffer is empty */
2674899054aSkre #endif
2684899054aSkre 
2694899054aSkre 	if (isatty(0)) {
2704899054aSkre 		read_tty = 1;
2714899054aSkre 		if (prompt) {
27261f28255Scgd 			out2str(prompt);
27361f28255Scgd 			flushall();
27461f28255Scgd 		}
2754899054aSkre #ifndef SMALL
2764899054aSkre 		b_flag = 1;	/* always buffer reads from ttys */
2774803d37fSdsl 
2784899054aSkre 		if (n_flag || end != '\n')
2794899054aSkre 			setraw = setrawmode(0, 1 + n_flag, end, &ttystate);
2804899054aSkre #endif
2814899054aSkre 	}
2824899054aSkre 
28361f28255Scgd 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
2844803d37fSdsl 		ifs = " \t\n";
2854803d37fSdsl 
28628b2f4e3Skre 	setstackmark(&mk);
28761f28255Scgd 	status = 0;
2884803d37fSdsl 	startword = 2;
28961f28255Scgd 	STARTSTACKSTR(p);
2904899054aSkre 
2914899054aSkre #ifdef SMALL
29261f28255Scgd 	for ( ; ; ) {
2934899054aSkre #else
2944899054aSkre 	for ( ; !n_flag || --maxlen >= 0 ; ) {
2954899054aSkre #endif
2964899054aSkre 		if ((c = next_read_char(0, maxlen + 1)) < 0) {
29761f28255Scgd 			status = 1;
29861f28255Scgd 			break;
29961f28255Scgd 		}
3004899054aSkre 		if (c == '\\' && c != end && !r_flag) {
3014899054aSkre #ifndef SMALL
3024899054aSkre 			if (n_flag && --maxlen < 0)
3034899054aSkre 				break;
3044899054aSkre #endif
3054899054aSkre 			if ((c = next_read_char(0, maxlen + 1)) < 0) {
30661f28255Scgd 				status = 1;
30761f28255Scgd 				break;
30861f28255Scgd 			}
309e2731928Skre 			if (c != '\n')	/* \ \n is always just removed */
310143e8d55Skre 				goto wdch;
3114899054aSkre 			if (read_tty)
3124899054aSkre 				out2str(getprompt(NULL));
3134803d37fSdsl 			continue;
3144803d37fSdsl 		}
315e2731928Skre 		if (c == end)
3164803d37fSdsl 			break;
317e2731928Skre 		if (c == '\0')
318e2731928Skre 			continue;
3194803d37fSdsl 		if (strchr(ifs, c))
3204803d37fSdsl 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
3214803d37fSdsl 		else
3224803d37fSdsl 			is_ifs = 0;
3234803d37fSdsl 
3244803d37fSdsl 		if (startword != 0) {
3254803d37fSdsl 			if (is_ifs == 1) {
3264803d37fSdsl 				/* Ignore leading IFS whitespace */
3274803d37fSdsl 				if (saveall)
3284803d37fSdsl 					STPUTC(c, p);
3294803d37fSdsl 				continue;
3304803d37fSdsl 			}
3314803d37fSdsl 			if (is_ifs == 2 && startword == 1) {
3324803d37fSdsl 				/* Only one non-whitespace IFS per word */
3334803d37fSdsl 				startword = 2;
3344803d37fSdsl 				if (saveall)
3354803d37fSdsl 					STPUTC(c, p);
3364803d37fSdsl 				continue;
3374803d37fSdsl 			}
3384803d37fSdsl 		}
3394803d37fSdsl 
3404803d37fSdsl 		if (is_ifs == 0) {
341143e8d55Skre   wdch:;
342e2731928Skre 			if (c == '\0') /* always ignore attempts to input \0 */
343e2731928Skre 				continue;
3444803d37fSdsl 			/* append this character to the current variable */
3454803d37fSdsl 			startword = 0;
3464803d37fSdsl 			if (saveall)
3474803d37fSdsl 				/* Not just a spare terminator */
3484803d37fSdsl 				saveall++;
3494803d37fSdsl 			STPUTC(c, p);
350143e8d55Skre 			wordlen = p - stackblock();
3514803d37fSdsl 			continue;
3524803d37fSdsl 		}
3534803d37fSdsl 
3544803d37fSdsl 		/* end of variable... */
3554803d37fSdsl 		startword = is_ifs;
3564803d37fSdsl 
3574803d37fSdsl 		if (ap[1] == NULL) {
3584803d37fSdsl 			/* Last variable needs all IFS chars */
3594803d37fSdsl 			saveall++;
3604803d37fSdsl 			STPUTC(c, p);
3614803d37fSdsl 			continue;
3624803d37fSdsl 		}
3634803d37fSdsl 
36428b2f4e3Skre 		if (equal(*ap, "IFS")) {
36528b2f4e3Skre 			/*
36628b2f4e3Skre 			 * we must not alter the value of IFS, as our
36728b2f4e3Skre 			 * local "ifs" var is (perhaps) pointing at it,
36828b2f4e3Skre 			 * at best we would be using data after free()
36928b2f4e3Skre 			 * the next time we reference ifs - but that mem
37028b2f4e3Skre 			 * may have been reused for something different.
37128b2f4e3Skre 			 *
37228b2f4e3Skre 			 * note that this might occur several times
37328b2f4e3Skre 			 */
37428b2f4e3Skre 			STPUTC('\0', p);
37528b2f4e3Skre 			newifs = grabstackstr(p);
37628b2f4e3Skre 		} else {
37761f28255Scgd 			STACKSTRNUL(p);
37861f28255Scgd 			setvar(*ap, stackblock(), 0);
37928b2f4e3Skre 		}
38061f28255Scgd 		ap++;
38161f28255Scgd 		STARTSTACKSTR(p);
382143e8d55Skre 		wordlen = 0;
38361f28255Scgd 	}
38461f28255Scgd 	STACKSTRNUL(p);
3854803d37fSdsl 
3864899054aSkre #ifndef SMALL
3874899054aSkre 	(void)next_read_char(0, (size_t)-1);	/* attempt to seek back */
3884899054aSkre 	if (setraw)
3894899054aSkre 		setrawmode(0, 0, end, &ttystate);
3904899054aSkre #endif
3914899054aSkre 
3924899054aSkre 
3934803d37fSdsl 	/* Remove trailing IFS chars */
394143e8d55Skre 	for (; stackblock() + wordlen <= --p; *p = 0) {
3954803d37fSdsl 		if (!strchr(ifs, *p))
3964803d37fSdsl 			break;
3974803d37fSdsl 		if (strchr(" \t\n", *p))
3984803d37fSdsl 			/* Always remove whitespace */
3994803d37fSdsl 			continue;
4004803d37fSdsl 		if (saveall > 1)
4014803d37fSdsl 			/* Don't remove non-whitespace unless it was naked */
4024803d37fSdsl 			break;
4034803d37fSdsl 	}
40428b2f4e3Skre 
40528b2f4e3Skre 	/*
40628b2f4e3Skre 	 * If IFS was one of the variables named, we can finally set it now
40728b2f4e3Skre 	 * (no further references to ifs will be made)
40828b2f4e3Skre 	 */
40928b2f4e3Skre 	if (newifs != NULL)
41028b2f4e3Skre 		setvar("IFS", newifs, 0);
41128b2f4e3Skre 
41228b2f4e3Skre 	/*
41328b2f4e3Skre 	 * Now we can assign to the final variable (which might
41428b2f4e3Skre 	 * also be IFS, hence the ordering here)
41528b2f4e3Skre 	 */
41661f28255Scgd 	setvar(*ap, stackblock(), 0);
4174803d37fSdsl 
4184803d37fSdsl 	/* Set any remaining args to "" */
41961f28255Scgd 	while (*++ap != NULL)
42061f28255Scgd 		setvar(*ap, nullstr, 0);
42128b2f4e3Skre 
42228b2f4e3Skre 	popstackmark(&mk);
42361f28255Scgd 	return status;
42461f28255Scgd }
42561f28255Scgd 
42661f28255Scgd 
42761f28255Scgd 
4284ce0d34aScgd int
429c02b3bbdSchristos umaskcmd(int argc, char **argv)
4304ce0d34aScgd {
431759eadefSjtc 	char *ap;
432d17e063cSkre 	mode_t mask;
43361f28255Scgd 	int i;
434759eadefSjtc 	int symbolic_mode = 0;
43561f28255Scgd 
436759eadefSjtc 	while ((i = nextopt("S")) != '\0') {
437759eadefSjtc 		symbolic_mode = 1;
438759eadefSjtc 	}
439759eadefSjtc 
44061f28255Scgd 	INTOFF;
44161f28255Scgd 	mask = umask(0);
44261f28255Scgd 	umask(mask);
44361f28255Scgd 	INTON;
444759eadefSjtc 
445759eadefSjtc 	if ((ap = *argptr) == NULL) {
446759eadefSjtc 		if (symbolic_mode) {
447759eadefSjtc 			char u[4], g[4], o[4];
448759eadefSjtc 
449759eadefSjtc 			i = 0;
450759eadefSjtc 			if ((mask & S_IRUSR) == 0)
451759eadefSjtc 				u[i++] = 'r';
452759eadefSjtc 			if ((mask & S_IWUSR) == 0)
453759eadefSjtc 				u[i++] = 'w';
454759eadefSjtc 			if ((mask & S_IXUSR) == 0)
455759eadefSjtc 				u[i++] = 'x';
456759eadefSjtc 			u[i] = '\0';
457759eadefSjtc 
458759eadefSjtc 			i = 0;
459759eadefSjtc 			if ((mask & S_IRGRP) == 0)
460759eadefSjtc 				g[i++] = 'r';
461759eadefSjtc 			if ((mask & S_IWGRP) == 0)
462759eadefSjtc 				g[i++] = 'w';
463759eadefSjtc 			if ((mask & S_IXGRP) == 0)
464759eadefSjtc 				g[i++] = 'x';
465759eadefSjtc 			g[i] = '\0';
466759eadefSjtc 
467759eadefSjtc 			i = 0;
468759eadefSjtc 			if ((mask & S_IROTH) == 0)
469759eadefSjtc 				o[i++] = 'r';
470759eadefSjtc 			if ((mask & S_IWOTH) == 0)
471759eadefSjtc 				o[i++] = 'w';
472759eadefSjtc 			if ((mask & S_IXOTH) == 0)
473759eadefSjtc 				o[i++] = 'x';
474759eadefSjtc 			o[i] = '\0';
475759eadefSjtc 
476759eadefSjtc 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
47761f28255Scgd 		} else {
478759eadefSjtc 			out1fmt("%.4o\n", mask);
479759eadefSjtc 		}
480759eadefSjtc 	} else {
481f792cdd7Schristos 		if (isdigit((unsigned char)*ap)) {
482d17e063cSkre 			int range = 0;
483d17e063cSkre 
48461f28255Scgd 			mask = 0;
48561f28255Scgd 			do {
486759eadefSjtc 				if (*ap >= '8' || *ap < '0')
48794d6cf1cSkre 					error("Not a valid octal number: '%s'",
488d17e063cSkre 					    *argptr);
489759eadefSjtc 				mask = (mask << 3) + (*ap - '0');
490d17e063cSkre 				if (mask & ~07777)
491d17e063cSkre 					range = 1;
492759eadefSjtc 			} while (*++ap != '\0');
493d17e063cSkre 			if (range)
494d17e063cSkre 			    error("Mask constant '%s' out of range", *argptr);
49561f28255Scgd 			umask(mask);
496759eadefSjtc 		} else {
497759eadefSjtc 			void *set;
498759eadefSjtc 
499cc484b78Sitohy 			INTOFF;
500cc484b78Sitohy 			if ((set = setmode(ap)) != 0) {
501759eadefSjtc 				mask = getmode(set, ~mask & 0777);
502cc484b78Sitohy 				ckfree(set);
503cc484b78Sitohy 			}
504cc484b78Sitohy 			INTON;
505cc484b78Sitohy 			if (!set)
506c05266afSchristos 				error("Cannot set mode `%s' (%s)", ap,
507c05266afSchristos 				    strerror(errno));
508cc484b78Sitohy 
509759eadefSjtc 			umask(~mask & 0777);
510759eadefSjtc 		}
51161f28255Scgd 	}
512b65a5b90Skre 	flushout(out1);
513b65a5b90Skre 	if (io_err(out1)) {
514b65a5b90Skre 		out2str("umask: I/O error\n");
515b65a5b90Skre 		return 1;
516b65a5b90Skre 	}
51761f28255Scgd 	return 0;
51861f28255Scgd }
51907bae7edSchristos 
52007bae7edSchristos /*
52107bae7edSchristos  * ulimit builtin
52207bae7edSchristos  *
52307bae7edSchristos  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
52407bae7edSchristos  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
52507bae7edSchristos  * ash by J.T. Conklin.
52607bae7edSchristos  *
52707bae7edSchristos  * Public domain.
52807bae7edSchristos  */
52907bae7edSchristos 
53007bae7edSchristos struct limits {
53107bae7edSchristos 	const char *name;
53213d04b59Schristos 	const char *unit;
53307bae7edSchristos 	char	option;
534f378cfe5Skre 	int8_t	cmd;		/* all RLIMIT_xxx are <= 127 */
535f378cfe5Skre 	unsigned short factor;	/* multiply by to get rlim_{cur,max} values */
53607bae7edSchristos };
53707bae7edSchristos 
538f378cfe5Skre #define	OPTSTRING_BASE "HSa"
539f378cfe5Skre 
54007bae7edSchristos static const struct limits limits[] = {
54107bae7edSchristos #ifdef RLIMIT_CPU
542f378cfe5Skre 	{ "time",	"seconds",	't',	RLIMIT_CPU,	   1 },
543f378cfe5Skre #define	OPTSTRING_t	OPTSTRING_BASE "t"
544f378cfe5Skre #else
545f378cfe5Skre #define	OPTSTRING_t	OPTSTRING_BASE
54607bae7edSchristos #endif
54707bae7edSchristos #ifdef RLIMIT_FSIZE
548f378cfe5Skre 	{ "file",	"blocks",	'f',	RLIMIT_FSIZE,	 512 },
549f378cfe5Skre #define	OPTSTRING_f	OPTSTRING_t "f"
550f378cfe5Skre #else
551f378cfe5Skre #define	OPTSTRING_f	OPTSTRING_t
55207bae7edSchristos #endif
55307bae7edSchristos #ifdef RLIMIT_DATA
554f378cfe5Skre 	{ "data",	"kbytes",	'd',	RLIMIT_DATA,	1024 },
555f378cfe5Skre #define	OPTSTRING_d	OPTSTRING_f "d"
556f378cfe5Skre #else
557f378cfe5Skre #define	OPTSTRING_d	OPTSTRING_f
55807bae7edSchristos #endif
55907bae7edSchristos #ifdef RLIMIT_STACK
560f378cfe5Skre 	{ "stack",	"kbytes",	's',	RLIMIT_STACK,	1024 },
561f378cfe5Skre #define	OPTSTRING_s	OPTSTRING_d "s"
562f378cfe5Skre #else
563f378cfe5Skre #define	OPTSTRING_s	OPTSTRING_d
56407bae7edSchristos #endif
56507bae7edSchristos #ifdef RLIMIT_CORE
566f378cfe5Skre 	{ "coredump",	"blocks",	'c',	RLIMIT_CORE,	 512 },
567f378cfe5Skre #define	OPTSTRING_c	OPTSTRING_s "c"
568f378cfe5Skre #else
569f378cfe5Skre #define	OPTSTRING_c	OPTSTRING_s
57007bae7edSchristos #endif
57107bae7edSchristos #ifdef RLIMIT_RSS
572f378cfe5Skre 	{ "memory",	"kbytes",	'm',	RLIMIT_RSS,	1024 },
573f378cfe5Skre #define	OPTSTRING_m	OPTSTRING_c "m"
574f378cfe5Skre #else
575f378cfe5Skre #define	OPTSTRING_m	OPTSTRING_c
57607bae7edSchristos #endif
57707bae7edSchristos #ifdef RLIMIT_MEMLOCK
578f378cfe5Skre 	{ "locked memory","kbytes",	'l',	RLIMIT_MEMLOCK, 1024 },
579f378cfe5Skre #define	OPTSTRING_l	OPTSTRING_m "l"
580f378cfe5Skre #else
581f378cfe5Skre #define	OPTSTRING_l	OPTSTRING_m
58207bae7edSchristos #endif
583426530ccSchristos #ifdef RLIMIT_NTHR
584f378cfe5Skre 	{ "thread",	"threads",	'r',	RLIMIT_NTHR,       1 },
585f378cfe5Skre #define	OPTSTRING_r	OPTSTRING_l "r"
586f378cfe5Skre #else
587f378cfe5Skre #define	OPTSTRING_r	OPTSTRING_l
588426530ccSchristos #endif
58907bae7edSchristos #ifdef RLIMIT_NPROC
590f378cfe5Skre 	{ "process",	"processes",	'p',	RLIMIT_NPROC,      1 },
591f378cfe5Skre #define	OPTSTRING_p	OPTSTRING_r "p"
592f378cfe5Skre #else
593f378cfe5Skre #define	OPTSTRING_p	OPTSTRING_r
59407bae7edSchristos #endif
59507bae7edSchristos #ifdef RLIMIT_NOFILE
596f378cfe5Skre 	{ "nofiles",	"descriptors",	'n',	RLIMIT_NOFILE,     1 },
597f378cfe5Skre #define	OPTSTRING_n	OPTSTRING_p "n"
598f378cfe5Skre #else
599f378cfe5Skre #define	OPTSTRING_n	OPTSTRING_p
60007bae7edSchristos #endif
60107bae7edSchristos #ifdef RLIMIT_VMEM
602f378cfe5Skre 	{ "vmemory",	"kbytes",	'v',	RLIMIT_VMEM,	1024 },
603f378cfe5Skre #define	OPTSTRING_v	OPTSTRING_n "v"
604f378cfe5Skre #else
605f378cfe5Skre #define	OPTSTRING_v	OPTSTRING_n
60607bae7edSchristos #endif
60707bae7edSchristos #ifdef RLIMIT_SWAP
608f378cfe5Skre 	{ "swap",	"kbytes",	'w',	RLIMIT_SWAP,	1024 },
609f378cfe5Skre #define	OPTSTRING_w	OPTSTRING_v "w"
610f378cfe5Skre #else
611f378cfe5Skre #define	OPTSTRING_w	OPTSTRING_v
61207bae7edSchristos #endif
6136acf809eSchristos #ifdef RLIMIT_SBSIZE
614f378cfe5Skre 	{ "sbsize",	"bytes",	'b',	RLIMIT_SBSIZE,	   1 },
615f378cfe5Skre #define	OPTSTRING_b	OPTSTRING_w "b"
616f378cfe5Skre #else
617f378cfe5Skre #define	OPTSTRING_b	OPTSTRING_w
6186acf809eSchristos #endif
619f378cfe5Skre 	{ NULL,		NULL,		'\0',	0,		   0 }
62007bae7edSchristos };
621f378cfe5Skre #define	OPTSTRING	OPTSTRING_b
62207bae7edSchristos 
62307bae7edSchristos int
624c02b3bbdSchristos ulimitcmd(int argc, char **argv)
62507bae7edSchristos {
62648250187Stls 	int	c;
627cd799663Schristos 	rlim_t val = 0;
62807bae7edSchristos 	enum { SOFT = 0x1, HARD = 0x2 }
629c15b76c2Skre 			how = 0, which;
63007bae7edSchristos 	const struct limits	*l;
63107bae7edSchristos 	int		set, all = 0;
63207bae7edSchristos 	int		optc, what;
63307bae7edSchristos 	struct rlimit	limit;
63407bae7edSchristos 
63507bae7edSchristos 	what = 'f';
636f378cfe5Skre 	while ((optc = nextopt(OPTSTRING)) != '\0')
63707bae7edSchristos 		switch (optc) {
63807bae7edSchristos 		case 'H':
639c15b76c2Skre 			how |= HARD;
64007bae7edSchristos 			break;
64107bae7edSchristos 		case 'S':
642c15b76c2Skre 			how |= SOFT;
64307bae7edSchristos 			break;
64407bae7edSchristos 		case 'a':
64507bae7edSchristos 			all = 1;
64607bae7edSchristos 			break;
64707bae7edSchristos 		default:
64807bae7edSchristos 			what = optc;
64907bae7edSchristos 		}
65007bae7edSchristos 
65107bae7edSchristos 	for (l = limits; l->name && l->option != what; l++)
65207bae7edSchristos 		;
65307bae7edSchristos 	if (!l->name)
6541d9dab3eSchristos 		error("internal error (%c)", what);
65507bae7edSchristos 
65607bae7edSchristos 	set = *argptr ? 1 : 0;
65707bae7edSchristos 	if (set) {
65807bae7edSchristos 		char *p = *argptr;
65907bae7edSchristos 
66007bae7edSchristos 		if (all || argptr[1])
6611d9dab3eSchristos 			error("too many arguments");
662c15b76c2Skre 		if (how == 0)
663c15b76c2Skre 			how = HARD | SOFT;
664c15b76c2Skre 
66507bae7edSchristos 		if (strcmp(p, "unlimited") == 0)
66607bae7edSchristos 			val = RLIM_INFINITY;
66707bae7edSchristos 		else {
66888fc62feSjtc 			val = (rlim_t) 0;
66907bae7edSchristos 
670c15b76c2Skre 			while ((c = *p++) >= '0' && c <= '9') {
671c15b76c2Skre 				if (val >= RLIM_INFINITY/10)
672f378cfe5Skre 					error("%s: value overflow", *argptr);
673c15b76c2Skre 				val = (val * 10);
674c15b76c2Skre 				if (val >= RLIM_INFINITY - (long)(c - '0'))
675f378cfe5Skre 					error("%s: value overflow", *argptr);
676c15b76c2Skre 				val += (long)(c - '0');
677c15b76c2Skre 			}
67807bae7edSchristos 			if (c)
679f378cfe5Skre 				error("%s: bad number", *argptr);
680c15b76c2Skre 			if (val > RLIM_INFINITY / l->factor)
681f378cfe5Skre 				error("%s: value overflow", *argptr);
68207bae7edSchristos 			val *= l->factor;
68307bae7edSchristos 		}
684c15b76c2Skre 	} else if (how == 0)
685c15b76c2Skre 		how = SOFT;
686c15b76c2Skre 
68707bae7edSchristos 	if (all) {
68807bae7edSchristos 		for (l = limits; l->name; l++) {
68907bae7edSchristos 			getrlimit(l->cmd, &limit);
69013d04b59Schristos 			out1fmt("%-13s (-%c %-11s)    ", l->name, l->option,
69113d04b59Schristos 			    l->unit);
692c15b76c2Skre 
693c15b76c2Skre 			which = how;
694c15b76c2Skre 			while (which != 0) {
695c15b76c2Skre 				if (which & SOFT) {
696c15b76c2Skre 					val = limit.rlim_cur;
697c15b76c2Skre 					which &= ~SOFT;
698c15b76c2Skre 				} else if (which & HARD) {
699c15b76c2Skre 					val = limit.rlim_max;
700c15b76c2Skre 					which &= ~HARD;
701c15b76c2Skre 				}
702c15b76c2Skre 
70307bae7edSchristos 				if (val == RLIM_INFINITY)
704c15b76c2Skre 					out1fmt("unlimited");
705c15b76c2Skre 				else {
70607bae7edSchristos 					val /= l->factor;
707360e930dSchristos #ifdef BSD4_4
708c15b76c2Skre 					out1fmt("%9lld", (long long) val);
709360e930dSchristos #else
710c15b76c2Skre 					out1fmt("%9ld", (long) val);
711360e930dSchristos #endif
71207bae7edSchristos 				}
713c15b76c2Skre 				out1fmt("%c", which ? '\t' : '\n');
714c15b76c2Skre 			}
71507bae7edSchristos 		}
716b65a5b90Skre 		goto done;
71707bae7edSchristos 	}
71807bae7edSchristos 
7199c27fd2aSchristos 	if (getrlimit(l->cmd, &limit) == -1)
7209c27fd2aSchristos 		error("error getting limit (%s)", strerror(errno));
72107bae7edSchristos 	if (set) {
72207bae7edSchristos 		if (how & HARD)
72307bae7edSchristos 			limit.rlim_max = val;
7241d9dab3eSchristos 		if (how & SOFT)
7251d9dab3eSchristos 			limit.rlim_cur = val;
72607bae7edSchristos 		if (setrlimit(l->cmd, &limit) < 0)
7271d9dab3eSchristos 			error("error setting limit (%s)", strerror(errno));
728c15b76c2Skre 		if (l->cmd == RLIMIT_NOFILE)
729c15b76c2Skre 			user_fd_limit = sysconf(_SC_OPEN_MAX);
73007bae7edSchristos 	} else {
73107bae7edSchristos 		if (how & SOFT)
73207bae7edSchristos 			val = limit.rlim_cur;
73307bae7edSchristos 		else if (how & HARD)
73407bae7edSchristos 			val = limit.rlim_max;
73507bae7edSchristos 
73607bae7edSchristos 		if (val == RLIM_INFINITY)
73707bae7edSchristos 			out1fmt("unlimited\n");
73807bae7edSchristos 		else
73907bae7edSchristos 		{
74007bae7edSchristos 			val /= l->factor;
741360e930dSchristos #ifdef BSD4_4
742f819878cSlukem 			out1fmt("%lld\n", (long long) val);
743360e930dSchristos #else
744360e930dSchristos 			out1fmt("%ld\n", (long) val);
745360e930dSchristos #endif
74607bae7edSchristos 		}
74707bae7edSchristos 	}
748b65a5b90Skre   done:;
749b65a5b90Skre 	flushout(out1);
750b65a5b90Skre 	if (io_err(out1)) {
751b65a5b90Skre 		out2str("ulimit: I/O error (stdout)\n");
752b65a5b90Skre 		return 1;
753b65a5b90Skre 	}
75407bae7edSchristos 	return 0;
75507bae7edSchristos }
756