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
setrawmode(int fd,int on,int end,struct termios * t)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
is_a_pipe(int fd)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
next_read_char(int fd,size_t max)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
next_read_char(int fd,size_t max)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
readcmd(int argc,char ** argv)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