xref: /netbsd-src/bin/sh/miscbltin.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*	$NetBSD: miscbltin.c,v 1.56 2024/10/12 23:34:56 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: miscbltin.c,v 1.56 2024/10/12 23:34:56 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 /*
45  * Miscellaneous builtins.
46  */
47 
48 #include <sys/param.h>		/* BSD4_4 */
49 #include <sys/resource.h>
50 #include <sys/stat.h>
51 #include <sys/time.h>
52 #include <sys/types.h>		/* quad_t */
53 
54 #include <ctype.h>
55 #include <errno.h>
56 #include <limits.h>
57 #include <stdlib.h>
58 #ifndef SMALL
59 #include <termios.h>
60 #endif
61 #include <unistd.h>
62 
63 #include "shell.h"
64 #include "options.h"
65 #include "var.h"
66 #include "input.h"		/* for whichprompt */
67 #include "output.h"
68 #include "parser.h"		/* for getprompt() */
69 #include "memalloc.h"
70 #include "error.h"
71 #include "builtins.h"
72 #include "mystring.h"
73 #include "redir.h"		/* for user_fd_limit */
74 
75 /*
76  * The read builtin.
77  * Backslashes escape the next char unless -r is specified.
78  *
79  * This uses unbuffered input, which may be avoidable in some cases.
80  *
81  * Note that if IFS=' :' then read x y should work so that:
82  * 'a b'	x='a', y='b'
83  * ' a b '	x='a', y='b'
84  * ':b'		x='',  y='b'
85  * ':'		x='',  y=''
86  * '::'		x='',  y=''
87  * ': :'	x='',  y=''
88  * ':::'	x='',  y='::'
89  * ':b c:'	x='',  y='b c:'
90  */
91 
92 #ifndef SMALL
93 static int b_flag;
94 
95 static int
96 setrawmode(int fd, int on, int end, struct termios *t)
97 {
98 	struct termios n;
99 
100 	if (on) {
101 		if (tcgetattr(fd, t) != 0)
102 			return 0;
103 		n = *t;
104 		if (on == 1 && b_flag) {
105 			n.c_cc[VEOL] = end;
106 		} else {
107 			cfmakeraw(&n);
108 			n.c_iflag |= ICRNL;
109 			n.c_oflag = t->c_oflag;
110 			n.c_lflag |= ECHO | ISIG;
111 		}
112 		if (tcsetattr(fd, TCSADRAIN | TCSASOFT, &n) == 0)
113 			return 1;
114 	} else
115 		(void)tcsetattr(fd, TCSADRAIN | TCSASOFT, t);
116 	return 0;
117 }
118 
119 static int
120 is_a_pipe(int fd)
121 {
122 	if (lseek(fd, 0, SEEK_CUR) == -1 && errno == ESPIPE) {
123 		errno = 0;
124 		return 1;
125 	}
126 	return 0;
127 }
128 
129 #define	READ_BUFFER_SIZE	512
130 
131 static int
132 next_read_char(int fd, size_t max)
133 {
134 	static char buffer[READ_BUFFER_SIZE];
135 	static int pos = 0, len = 0;
136 
137 	if (max == 0) {
138 		pos = len = 0;
139 		return -1;
140 	}
141 	if (max == (size_t)-1) {
142 		/*
143 		 * If possible, and necessary, rewind the file
144 		 * so unprocessed data  can be read again next time
145 		 *
146 		 * If that fails, never mind (-b allows that to happen)
147 		 */
148 		if (b_flag && pos < len)
149 			(void)lseek(fd, (off_t)(pos - len), SEEK_CUR);
150 		return -1;
151 	}
152 
153 	if (b_flag == 0) {
154 		char c;
155 
156 		(void) max;
157 		if (read(fd, &c, 1) != 1)
158 			return -1;
159 		return (c & 0xFF);
160 	}
161 
162 	if (pos >= len) {
163 		pos = 0;
164 		if (max > sizeof buffer)
165 			max = sizeof buffer;
166 		len = read(fd, buffer, max);
167 		if (len <= 0)
168 			return -1;
169 	}
170 
171 	return buffer[pos++] & 0xFF;
172 }
173 
174 #define READ_OPTS	"bd:n:p:r"
175 
176 #else
177 
178 static inline int
179 next_read_char(int fd, size_t max)
180 {
181 	char c;
182 
183 	if (max == 0 || max == (size_t)-1)
184 		return 0;
185 
186 	if (read(fd, &c, 1) != 1)
187 		return -1;
188 	return (c & 0xFF);
189 }
190 
191 #define n_flag 0
192 #define maxlen 0
193 
194 #define READ_OPTS	"d:p:r"
195 
196 #endif
197 
198 int
199 readcmd(int argc, char **argv)
200 {
201 	char **ap;
202 	int c;
203 	char end;
204 	int r_flag;
205 	char *prompt;
206 	const char *ifs;
207 	char *p;
208 	int startword;
209 	int status;
210 	int i;
211 	int is_ifs;
212 	int saveall = 0;
213 	int read_tty = 0;
214 	ptrdiff_t wordlen = 0;
215 	char *newifs = NULL;
216 	struct stackmark mk;
217 
218 #ifndef SMALL
219 	struct termios ttystate;
220 	int n_flag, maxlen;
221 	int setraw = 0;
222 
223 	b_flag = 0;
224 	n_flag = 0;
225 	maxlen = READ_BUFFER_SIZE - 1;
226 #endif
227 
228 	end = '\n';				/* record delimiter */
229 	r_flag = 0;
230 	prompt = NULL;
231 	whichprompt = 2;			/* for continuation lines */
232 
233 	while ((i = nextopt(READ_OPTS)) != '\0') {
234 		switch (i) {
235 		case 'd':
236 			end = *optionarg;	/* even if '\0' */
237 			break;
238 		case 'p':
239 			prompt = optionarg;
240 			break;
241 		case 'r':
242 			r_flag = 1;
243 			break;
244 #ifndef SMALL
245 		case 'n':
246 			maxlen = number(optionarg);
247 			if (maxlen > (INT_MAX >> 8) + 1)	/* sanity */
248 				error("-n %s too large", optionarg);
249 			n_flag = 1;
250 			break;
251 		case 'b':
252 			if (!is_a_pipe(0))
253 				b_flag = 1;
254 			break;
255 #endif
256 		}
257 	}
258 
259 	if (*(ap = argptr) == NULL)
260 		error("variable name required\n"
261 #ifdef SMALL
262 		      "Usage: read [-r] [-d C] [-p prompt] var...");
263 #else
264 		      "Usage: read [-br] [-d C] [-n len] [-p prompt] var...");
265 
266 	(void)next_read_char(0, 0);	/* make sure the buffer is empty */
267 #endif
268 
269 	if (isatty(0)) {
270 		read_tty = 1;
271 		if (prompt) {
272 			out2str(prompt);
273 			flushall();
274 		}
275 #ifndef SMALL
276 		b_flag = 1;	/* always buffer reads from ttys */
277 
278 		if (n_flag || end != '\n')
279 			setraw = setrawmode(0, 1 + n_flag, end, &ttystate);
280 #endif
281 	}
282 
283 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
284 		ifs = " \t\n";
285 
286 	setstackmark(&mk);
287 	status = 0;
288 	startword = 2;
289 	STARTSTACKSTR(p);
290 
291 #ifdef SMALL
292 	for ( ; ; ) {
293 #else
294 	for ( ; !n_flag || --maxlen >= 0 ; ) {
295 #endif
296 		if ((c = next_read_char(0, maxlen + 1)) < 0) {
297 			status = 1;
298 			break;
299 		}
300 		if (c == '\\' && c != end && !r_flag) {
301 #ifndef SMALL
302 			if (n_flag && --maxlen < 0)
303 				break;
304 #endif
305 			if ((c = next_read_char(0, maxlen + 1)) < 0) {
306 				status = 1;
307 				break;
308 			}
309 			if (c != '\n')	/* \ \n is always just removed */
310 				goto wdch;
311 			if (read_tty)
312 				out2str(getprompt(NULL));
313 			continue;
314 		}
315 		if (c == end)
316 			break;
317 		if (c == '\0')
318 			continue;
319 		if (strchr(ifs, c))
320 			is_ifs = strchr(" \t\n", c) ? 1 : 2;
321 		else
322 			is_ifs = 0;
323 
324 		if (startword != 0) {
325 			if (is_ifs == 1) {
326 				/* Ignore leading IFS whitespace */
327 				if (saveall)
328 					STPUTC(c, p);
329 				continue;
330 			}
331 			if (is_ifs == 2 && startword == 1) {
332 				/* Only one non-whitespace IFS per word */
333 				startword = 2;
334 				if (saveall)
335 					STPUTC(c, p);
336 				continue;
337 			}
338 		}
339 
340 		if (is_ifs == 0) {
341   wdch:;
342 			if (c == '\0') /* always ignore attempts to input \0 */
343 				continue;
344 			/* append this character to the current variable */
345 			startword = 0;
346 			if (saveall)
347 				/* Not just a spare terminator */
348 				saveall++;
349 			STPUTC(c, p);
350 			wordlen = p - stackblock();
351 			continue;
352 		}
353 
354 		/* end of variable... */
355 		startword = is_ifs;
356 
357 		if (ap[1] == NULL) {
358 			/* Last variable needs all IFS chars */
359 			saveall++;
360 			STPUTC(c, p);
361 			continue;
362 		}
363 
364 		if (equal(*ap, "IFS")) {
365 			/*
366 			 * we must not alter the value of IFS, as our
367 			 * local "ifs" var is (perhaps) pointing at it,
368 			 * at best we would be using data after free()
369 			 * the next time we reference ifs - but that mem
370 			 * may have been reused for something different.
371 			 *
372 			 * note that this might occur several times
373 			 */
374 			STPUTC('\0', p);
375 			newifs = grabstackstr(p);
376 		} else {
377 			STACKSTRNUL(p);
378 			setvar(*ap, stackblock(), 0);
379 		}
380 		ap++;
381 		STARTSTACKSTR(p);
382 		wordlen = 0;
383 	}
384 	STACKSTRNUL(p);
385 
386 #ifndef SMALL
387 	(void)next_read_char(0, (size_t)-1);	/* attempt to seek back */
388 	if (setraw)
389 		setrawmode(0, 0, end, &ttystate);
390 #endif
391 
392 
393 	/* Remove trailing IFS chars */
394 	for (; stackblock() + wordlen <= --p; *p = 0) {
395 		if (!strchr(ifs, *p))
396 			break;
397 		if (strchr(" \t\n", *p))
398 			/* Always remove whitespace */
399 			continue;
400 		if (saveall > 1)
401 			/* Don't remove non-whitespace unless it was naked */
402 			break;
403 	}
404 
405 	/*
406 	 * If IFS was one of the variables named, we can finally set it now
407 	 * (no further references to ifs will be made)
408 	 */
409 	if (newifs != NULL)
410 		setvar("IFS", newifs, 0);
411 
412 	/*
413 	 * Now we can assign to the final variable (which might
414 	 * also be IFS, hence the ordering here)
415 	 */
416 	setvar(*ap, stackblock(), 0);
417 
418 	/* Set any remaining args to "" */
419 	while (*++ap != NULL)
420 		setvar(*ap, nullstr, 0);
421 
422 	popstackmark(&mk);
423 	return status;
424 }
425 
426 
427 
428 int
429 umaskcmd(int argc, char **argv)
430 {
431 	char *ap;
432 	mode_t mask;
433 	int i;
434 	int symbolic_mode = 0;
435 
436 	while ((i = nextopt("S")) != '\0') {
437 		symbolic_mode = 1;
438 	}
439 
440 	INTOFF;
441 	mask = umask(0);
442 	umask(mask);
443 	INTON;
444 
445 	if ((ap = *argptr) == NULL) {
446 		if (symbolic_mode) {
447 			char u[4], g[4], o[4];
448 
449 			i = 0;
450 			if ((mask & S_IRUSR) == 0)
451 				u[i++] = 'r';
452 			if ((mask & S_IWUSR) == 0)
453 				u[i++] = 'w';
454 			if ((mask & S_IXUSR) == 0)
455 				u[i++] = 'x';
456 			u[i] = '\0';
457 
458 			i = 0;
459 			if ((mask & S_IRGRP) == 0)
460 				g[i++] = 'r';
461 			if ((mask & S_IWGRP) == 0)
462 				g[i++] = 'w';
463 			if ((mask & S_IXGRP) == 0)
464 				g[i++] = 'x';
465 			g[i] = '\0';
466 
467 			i = 0;
468 			if ((mask & S_IROTH) == 0)
469 				o[i++] = 'r';
470 			if ((mask & S_IWOTH) == 0)
471 				o[i++] = 'w';
472 			if ((mask & S_IXOTH) == 0)
473 				o[i++] = 'x';
474 			o[i] = '\0';
475 
476 			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
477 		} else {
478 			out1fmt("%.4o\n", mask);
479 		}
480 	} else {
481 		if (isdigit((unsigned char)*ap)) {
482 			int range = 0;
483 
484 			mask = 0;
485 			do {
486 				if (*ap >= '8' || *ap < '0')
487 					error("Not a valid octal number: '%s'",
488 					    *argptr);
489 				mask = (mask << 3) + (*ap - '0');
490 				if (mask & ~07777)
491 					range = 1;
492 			} while (*++ap != '\0');
493 			if (range)
494 			    error("Mask constant '%s' out of range", *argptr);
495 			umask(mask);
496 		} else {
497 			void *set;
498 
499 			INTOFF;
500 			if ((set = setmode(ap)) != 0) {
501 				mask = getmode(set, ~mask & 0777);
502 				ckfree(set);
503 			}
504 			INTON;
505 			if (!set)
506 				error("Cannot set mode `%s' (%s)", ap,
507 				    strerror(errno));
508 
509 			umask(~mask & 0777);
510 		}
511 	}
512 	flushout(out1);
513 	if (io_err(out1)) {
514 		out2str("umask: I/O error\n");
515 		return 1;
516 	}
517 	return 0;
518 }
519 
520 /*
521  * ulimit builtin
522  *
523  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
524  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
525  * ash by J.T. Conklin.
526  *
527  * Public domain.
528  */
529 
530 struct limits {
531 	const char *name;
532 	const char *unit;
533 	char	option;
534 	int8_t	cmd;		/* all RLIMIT_xxx are <= 127 */
535 	unsigned short factor;	/* multiply by to get rlim_{cur,max} values */
536 };
537 
538 #define	OPTSTRING_BASE "HSa"
539 
540 static const struct limits limits[] = {
541 #ifdef RLIMIT_CPU
542 	{ "time",	"seconds",	't',	RLIMIT_CPU,	   1 },
543 #define	OPTSTRING_t	OPTSTRING_BASE "t"
544 #else
545 #define	OPTSTRING_t	OPTSTRING_BASE
546 #endif
547 #ifdef RLIMIT_FSIZE
548 	{ "file",	"blocks",	'f',	RLIMIT_FSIZE,	 512 },
549 #define	OPTSTRING_f	OPTSTRING_t "f"
550 #else
551 #define	OPTSTRING_f	OPTSTRING_t
552 #endif
553 #ifdef RLIMIT_DATA
554 	{ "data",	"kbytes",	'd',	RLIMIT_DATA,	1024 },
555 #define	OPTSTRING_d	OPTSTRING_f "d"
556 #else
557 #define	OPTSTRING_d	OPTSTRING_f
558 #endif
559 #ifdef RLIMIT_STACK
560 	{ "stack",	"kbytes",	's',	RLIMIT_STACK,	1024 },
561 #define	OPTSTRING_s	OPTSTRING_d "s"
562 #else
563 #define	OPTSTRING_s	OPTSTRING_d
564 #endif
565 #ifdef RLIMIT_CORE
566 	{ "coredump",	"blocks",	'c',	RLIMIT_CORE,	 512 },
567 #define	OPTSTRING_c	OPTSTRING_s "c"
568 #else
569 #define	OPTSTRING_c	OPTSTRING_s
570 #endif
571 #ifdef RLIMIT_RSS
572 	{ "memory",	"kbytes",	'm',	RLIMIT_RSS,	1024 },
573 #define	OPTSTRING_m	OPTSTRING_c "m"
574 #else
575 #define	OPTSTRING_m	OPTSTRING_c
576 #endif
577 #ifdef RLIMIT_MEMLOCK
578 	{ "locked memory","kbytes",	'l',	RLIMIT_MEMLOCK, 1024 },
579 #define	OPTSTRING_l	OPTSTRING_m "l"
580 #else
581 #define	OPTSTRING_l	OPTSTRING_m
582 #endif
583 #ifdef RLIMIT_NTHR
584 	{ "thread",	"threads",	'r',	RLIMIT_NTHR,       1 },
585 #define	OPTSTRING_r	OPTSTRING_l "r"
586 #else
587 #define	OPTSTRING_r	OPTSTRING_l
588 #endif
589 #ifdef RLIMIT_NPROC
590 	{ "process",	"processes",	'p',	RLIMIT_NPROC,      1 },
591 #define	OPTSTRING_p	OPTSTRING_r "p"
592 #else
593 #define	OPTSTRING_p	OPTSTRING_r
594 #endif
595 #ifdef RLIMIT_NOFILE
596 	{ "nofiles",	"descriptors",	'n',	RLIMIT_NOFILE,     1 },
597 #define	OPTSTRING_n	OPTSTRING_p "n"
598 #else
599 #define	OPTSTRING_n	OPTSTRING_p
600 #endif
601 #ifdef RLIMIT_VMEM
602 	{ "vmemory",	"kbytes",	'v',	RLIMIT_VMEM,	1024 },
603 #define	OPTSTRING_v	OPTSTRING_n "v"
604 #else
605 #define	OPTSTRING_v	OPTSTRING_n
606 #endif
607 #ifdef RLIMIT_SWAP
608 	{ "swap",	"kbytes",	'w',	RLIMIT_SWAP,	1024 },
609 #define	OPTSTRING_w	OPTSTRING_v "w"
610 #else
611 #define	OPTSTRING_w	OPTSTRING_v
612 #endif
613 #ifdef RLIMIT_SBSIZE
614 	{ "sbsize",	"bytes",	'b',	RLIMIT_SBSIZE,	   1 },
615 #define	OPTSTRING_b	OPTSTRING_w "b"
616 #else
617 #define	OPTSTRING_b	OPTSTRING_w
618 #endif
619 	{ NULL,		NULL,		'\0',	0,		   0 }
620 };
621 #define	OPTSTRING	OPTSTRING_b
622 
623 int
624 ulimitcmd(int argc, char **argv)
625 {
626 	int	c;
627 	rlim_t val = 0;
628 	enum { SOFT = 0x1, HARD = 0x2 }
629 			how = 0, which;
630 	const struct limits	*l;
631 	int		set, all = 0;
632 	int		optc, what;
633 	struct rlimit	limit;
634 
635 	what = 'f';
636 	while ((optc = nextopt(OPTSTRING)) != '\0')
637 		switch (optc) {
638 		case 'H':
639 			how |= HARD;
640 			break;
641 		case 'S':
642 			how |= SOFT;
643 			break;
644 		case 'a':
645 			all = 1;
646 			break;
647 		default:
648 			what = optc;
649 		}
650 
651 	for (l = limits; l->name && l->option != what; l++)
652 		;
653 	if (!l->name)
654 		error("internal error (%c)", what);
655 
656 	set = *argptr ? 1 : 0;
657 	if (set) {
658 		char *p = *argptr;
659 
660 		if (all || argptr[1])
661 			error("too many arguments");
662 		if (how == 0)
663 			how = HARD | SOFT;
664 
665 		if (strcmp(p, "unlimited") == 0)
666 			val = RLIM_INFINITY;
667 		else {
668 			val = (rlim_t) 0;
669 
670 			while ((c = *p++) >= '0' && c <= '9') {
671 				if (val >= RLIM_INFINITY/10)
672 					error("%s: value overflow", *argptr);
673 				val = (val * 10);
674 				if (val >= RLIM_INFINITY - (long)(c - '0'))
675 					error("%s: value overflow", *argptr);
676 				val += (long)(c - '0');
677 			}
678 			if (c)
679 				error("%s: bad number", *argptr);
680 			if (val > RLIM_INFINITY / l->factor)
681 				error("%s: value overflow", *argptr);
682 			val *= l->factor;
683 		}
684 	} else if (how == 0)
685 		how = SOFT;
686 
687 	if (all) {
688 		for (l = limits; l->name; l++) {
689 			getrlimit(l->cmd, &limit);
690 			out1fmt("%-13s (-%c %-11s)    ", l->name, l->option,
691 			    l->unit);
692 
693 			which = how;
694 			while (which != 0) {
695 				if (which & SOFT) {
696 					val = limit.rlim_cur;
697 					which &= ~SOFT;
698 				} else if (which & HARD) {
699 					val = limit.rlim_max;
700 					which &= ~HARD;
701 				}
702 
703 				if (val == RLIM_INFINITY)
704 					out1fmt("unlimited");
705 				else {
706 					val /= l->factor;
707 #ifdef BSD4_4
708 					out1fmt("%9lld", (long long) val);
709 #else
710 					out1fmt("%9ld", (long) val);
711 #endif
712 				}
713 				out1fmt("%c", which ? '\t' : '\n');
714 			}
715 		}
716 		goto done;
717 	}
718 
719 	if (getrlimit(l->cmd, &limit) == -1)
720 		error("error getting limit (%s)", strerror(errno));
721 	if (set) {
722 		if (how & HARD)
723 			limit.rlim_max = val;
724 		if (how & SOFT)
725 			limit.rlim_cur = val;
726 		if (setrlimit(l->cmd, &limit) < 0)
727 			error("error setting limit (%s)", strerror(errno));
728 		if (l->cmd == RLIMIT_NOFILE)
729 			user_fd_limit = sysconf(_SC_OPEN_MAX);
730 	} else {
731 		if (how & SOFT)
732 			val = limit.rlim_cur;
733 		else if (how & HARD)
734 			val = limit.rlim_max;
735 
736 		if (val == RLIM_INFINITY)
737 			out1fmt("unlimited\n");
738 		else
739 		{
740 			val /= l->factor;
741 #ifdef BSD4_4
742 			out1fmt("%lld\n", (long long) val);
743 #else
744 			out1fmt("%ld\n", (long) val);
745 #endif
746 		}
747 	}
748   done:;
749 	flushout(out1);
750 	if (io_err(out1)) {
751 		out2str("ulimit: I/O error (stdout)\n");
752 		return 1;
753 	}
754 	return 0;
755 }
756