Merge branch 'vendor/DIFFUTILS'
[dragonfly.git] / bin / sh / miscbltin.c
1 /*-
2  * Copyright (c) 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#)miscbltin.c      8.4 (Berkeley) 5/4/95
37  * $FreeBSD: src/bin/sh/miscbltin.c,v 1.44 2010/11/23 22:17:39 jilles Exp $
38  */
39
40 /*
41  * Miscellaneous builtins.
42  */
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/time.h>
47 #include <sys/resource.h>
48 #include <unistd.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <stdint.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <termios.h>
55
56 #include "shell.h"
57 #include "options.h"
58 #include "var.h"
59 #include "output.h"
60 #include "memalloc.h"
61 #include "error.h"
62 #include "mystring.h"
63
64 int readcmd(int, char **);
65 int umaskcmd(int, char **);
66 int ulimitcmd(int, char **);
67
68 #undef eflag
69
70 /*
71  * The read builtin.  The -r option causes backslashes to be treated like
72  * ordinary characters.
73  *
74  * This uses unbuffered input, which may be avoidable in some cases.
75  *
76  * Note that if IFS=' :' then read x y should work so that:
77  * 'a b'        x='a', y='b'
78  * ' a b '      x='a', y='b'
79  * ':b'         x='',  y='b'
80  * ':'          x='',  y=''
81  * '::'         x='',  y=''
82  * ': :'        x='',  y=''
83  * ':::'        x='',  y='::'
84  * ':b c:'      x='',  y='b c:'
85  */
86
87 int
88 readcmd(int argc __unused, char **argv __unused)
89 {
90         char **ap;
91         int backslash;
92         char c;
93         int rflag;
94         const char *prompt;
95         const char *ifs;
96         char *p;
97         int startword;
98         int status;
99         int i;
100         int is_ifs;
101         int saveall = 0;
102         struct timeval tv;
103         char *tvptr;
104         fd_set ifds;
105
106         rflag = 0;
107         prompt = NULL;
108         tv.tv_sec = -1;
109         tv.tv_usec = 0;
110         while ((i = nextopt("erp:t:")) != '\0') {
111                 switch(i) {
112                 case 'p':
113                         prompt = shoptarg;
114                         break;
115                 case 'e':
116                         break;
117                 case 'r':
118                         rflag = 1;
119                         break;
120                 case 't':
121                         tv.tv_sec = strtol(shoptarg, &tvptr, 0);
122                         if (tvptr == shoptarg)
123                                 error("timeout value");
124                         switch(*tvptr) {
125                         case 0:
126                         case 's':
127                                 break;
128                         case 'h':
129                                 tv.tv_sec *= 60;
130                                 /* FALLTHROUGH */
131                         case 'm':
132                                 tv.tv_sec *= 60;
133                                 break;
134                         default:
135                                 error("timeout unit");
136                         }
137                         break;
138                 }
139         }
140         if (prompt && isatty(0)) {
141                 out2str(prompt);
142                 flushall();
143         }
144         if (*(ap = argptr) == NULL)
145                 error("arg count");
146         if ((ifs = bltinlookup("IFS", 1)) == NULL)
147                 ifs = " \t\n";
148
149         if (tv.tv_sec >= 0) {
150                 /*
151                  * Wait for something to become available.
152                  */
153                 FD_ZERO(&ifds);
154                 FD_SET(0, &ifds);
155                 status = select(1, &ifds, NULL, NULL, &tv);
156                 /*
157                  * If there's nothing ready, return an error.
158                  */
159                 if (status <= 0)
160                         return(1);
161         }
162
163         status = 0;
164         startword = 2;
165         backslash = 0;
166         STARTSTACKSTR(p);
167         for (;;) {
168                 if (read(STDIN_FILENO, &c, 1) != 1) {
169                         status = 1;
170                         break;
171                 }
172                 if (c == '\0')
173                         continue;
174                 CHECKSTRSPACE(1, p);
175                 if (backslash) {
176                         backslash = 0;
177                         startword = 0;
178                         if (c != '\n')
179                                 USTPUTC(c, p);
180                         continue;
181                 }
182                 if (!rflag && c == '\\') {
183                         backslash++;
184                         continue;
185                 }
186                 if (c == '\n')
187                         break;
188                 if (strchr(ifs, c))
189                         is_ifs = strchr(" \t\n", c) ? 1 : 2;
190                 else
191                         is_ifs = 0;
192
193                 if (startword != 0) {
194                         if (is_ifs == 1) {
195                                 /* Ignore leading IFS whitespace */
196                                 if (saveall)
197                                         USTPUTC(c, p);
198                                 continue;
199                         }
200                         if (is_ifs == 2 && startword == 1) {
201                                 /* Only one non-whitespace IFS per word */
202                                 startword = 2;
203                                 if (saveall)
204                                         USTPUTC(c, p);
205                                 continue;
206                         }
207                 }
208
209                 if (is_ifs == 0) {
210                         /* append this character to the current variable */
211                         startword = 0;
212                         if (saveall)
213                                 /* Not just a spare terminator */
214                                 saveall++;
215                         USTPUTC(c, p);
216                         continue;
217                 }
218
219                 /* end of variable... */
220                 startword = is_ifs;
221
222                 if (ap[1] == NULL) {
223                         /* Last variable needs all IFS chars */
224                         saveall++;
225                         USTPUTC(c, p);
226                         continue;
227                 }
228
229                 STACKSTRNUL(p);
230                 setvar(*ap, stackblock(), 0);
231                 ap++;
232                 STARTSTACKSTR(p);
233         }
234         STACKSTRNUL(p);
235
236         /* Remove trailing IFS chars */
237         for (; stackblock() <= --p; *p = 0) {
238                 if (!strchr(ifs, *p))
239                         break;
240                 if (strchr(" \t\n", *p))
241                         /* Always remove whitespace */
242                         continue;
243                 if (saveall > 1)
244                         /* Don't remove non-whitespace unless it was naked */
245                         break;
246         }
247         setvar(*ap, stackblock(), 0);
248
249         /* Set any remaining args to "" */
250         while (*++ap != NULL)
251                 setvar(*ap, nullstr, 0);
252         return status;
253 }
254
255
256
257 int
258 umaskcmd(int argc __unused, char **argv __unused)
259 {
260         char *ap;
261         int mask;
262         int i;
263         int symbolic_mode = 0;
264
265         while ((i = nextopt("S")) != '\0') {
266                 symbolic_mode = 1;
267         }
268
269         INTOFF;
270         mask = umask(0);
271         umask(mask);
272         INTON;
273
274         if ((ap = *argptr) == NULL) {
275                 if (symbolic_mode) {
276                         char u[4], g[4], o[4];
277
278                         i = 0;
279                         if ((mask & S_IRUSR) == 0)
280                                 u[i++] = 'r';
281                         if ((mask & S_IWUSR) == 0)
282                                 u[i++] = 'w';
283                         if ((mask & S_IXUSR) == 0)
284                                 u[i++] = 'x';
285                         u[i] = '\0';
286
287                         i = 0;
288                         if ((mask & S_IRGRP) == 0)
289                                 g[i++] = 'r';
290                         if ((mask & S_IWGRP) == 0)
291                                 g[i++] = 'w';
292                         if ((mask & S_IXGRP) == 0)
293                                 g[i++] = 'x';
294                         g[i] = '\0';
295
296                         i = 0;
297                         if ((mask & S_IROTH) == 0)
298                                 o[i++] = 'r';
299                         if ((mask & S_IWOTH) == 0)
300                                 o[i++] = 'w';
301                         if ((mask & S_IXOTH) == 0)
302                                 o[i++] = 'x';
303                         o[i] = '\0';
304
305                         out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
306                 } else {
307                         out1fmt("%.4o\n", mask);
308                 }
309         } else {
310                 if (isdigit(*ap)) {
311                         mask = 0;
312                         do {
313                                 if (*ap >= '8' || *ap < '0')
314                                         error("Illegal number: %s", *argptr);
315                                 mask = (mask << 3) + (*ap - '0');
316                         } while (*++ap != '\0');
317                         umask(mask);
318                 } else {
319                         void *set;
320                         INTOFF;
321                         if ((set = setmode (ap)) == 0)
322                                 error("Illegal number: %s", ap);
323
324                         mask = getmode (set, ~mask & 0777);
325                         umask(~mask & 0777);
326                         free(set);
327                         INTON;
328                 }
329         }
330         return 0;
331 }
332
333 /*
334  * ulimit builtin
335  *
336  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
337  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
338  * ash by J.T. Conklin.
339  *
340  * Public domain.
341  */
342
343 struct limits {
344         const char *name;
345         const char *units;
346         int     cmd;
347         int     factor; /* multiply by to get rlim_{cur,max} values */
348         char    option;
349 };
350
351 static const struct limits limits[] = {
352 #ifdef RLIMIT_CPU
353         { "cpu time",           "seconds",      RLIMIT_CPU,        1, 't' },
354 #endif
355 #ifdef RLIMIT_FSIZE
356         { "file size",          "512-blocks",   RLIMIT_FSIZE,    512, 'f' },
357 #endif
358 #ifdef RLIMIT_DATA
359         { "data seg size",      "kbytes",       RLIMIT_DATA,    1024, 'd' },
360 #endif
361 #ifdef RLIMIT_STACK
362         { "stack size",         "kbytes",       RLIMIT_STACK,   1024, 's' },
363 #endif
364 #ifdef  RLIMIT_CORE
365         { "core file size",     "512-blocks",   RLIMIT_CORE,     512, 'c' },
366 #endif
367 #ifdef RLIMIT_RSS
368         { "max memory size",    "kbytes",       RLIMIT_RSS,     1024, 'm' },
369 #endif
370 #ifdef RLIMIT_MEMLOCK
371         { "locked memory",      "kbytes",       RLIMIT_MEMLOCK, 1024, 'l' },
372 #endif
373 #ifdef RLIMIT_NPROC
374         { "max user processes", NULL,           RLIMIT_NPROC,      1, 'u' },
375 #endif
376 #ifdef RLIMIT_NOFILE
377         { "open files",         NULL,           RLIMIT_NOFILE,     1, 'n' },
378 #endif
379 #ifdef RLIMIT_VMEM
380         { "virtual mem size",   "kbytes",       RLIMIT_VMEM,    1024, 'v' },
381 #endif
382 #ifdef RLIMIT_SWAP
383         { "swap limit",         "kbytes",       RLIMIT_SWAP,    1024, 'w' },
384 #endif
385 #ifdef RLIMIT_SBSIZE
386         { "sbsize",             "bytes",        RLIMIT_SBSIZE,     1, 'b' },
387 #endif
388 #ifdef RLIMIT_POSIXLOCK
389         { "posixlocks",         NULL,           RLIMIT_POSIXLOCK,  1, 'k' },
390 #endif
391         { NULL,                 NULL,           0,                 0, '\0' }
392 };
393
394 int
395 ulimitcmd(int argc __unused, char **argv __unused)
396 {
397         int     c;
398         rlim_t val = 0;
399         enum { SOFT = 0x1, HARD = 0x2 }
400                         how = SOFT | HARD;
401         const struct limits     *l;
402         int             set, all = 0;
403         int             optc, what;
404         struct rlimit   limit;
405
406         what = 'f';
407         while ((optc = nextopt("HSatfdsmcnuvlbk")) != '\0')
408                 switch (optc) {
409                 case 'H':
410                         how = HARD;
411                         break;
412                 case 'S':
413                         how = SOFT;
414                         break;
415                 case 'a':
416                         all = 1;
417                         break;
418                 default:
419                         what = optc;
420                 }
421
422         for (l = limits; l->name && l->option != what; l++)
423                 ;
424         if (!l->name)
425                 error("internal error (%c)", what);
426
427         set = *argptr ? 1 : 0;
428         if (set) {
429                 char *p = *argptr;
430
431                 if (all || argptr[1])
432                         error("too many arguments");
433                 if (strcmp(p, "unlimited") == 0)
434                         val = RLIM_INFINITY;
435                 else {
436                         val = 0;
437
438                         while ((c = *p++) >= '0' && c <= '9')
439                         {
440                                 val = (val * 10) + (long)(c - '0');
441                                 if (val < 0)
442                                         break;
443                         }
444                         if (c)
445                                 error("bad number");
446                         val *= l->factor;
447                 }
448         }
449         if (all) {
450                 for (l = limits; l->name; l++) {
451                         char optbuf[40];
452                         if (getrlimit(l->cmd, &limit) < 0)
453                                 error("can't get limit: %s", strerror(errno));
454                         if (how & SOFT)
455                                 val = limit.rlim_cur;
456                         else if (how & HARD)
457                                 val = limit.rlim_max;
458
459                         if (l->units)
460                                 snprintf(optbuf, sizeof(optbuf),
461                                         "(%s, -%c) ", l->units, l->option);
462                         else
463                                 snprintf(optbuf, sizeof(optbuf),
464                                         "(-%c) ", l->option);
465                         out1fmt("%-18s %18s ", l->name, optbuf);
466                         if (val == RLIM_INFINITY)
467                                 out1fmt("unlimited\n");
468                         else
469                         {
470                                 val /= l->factor;
471                                 out1fmt("%jd\n", (intmax_t)val);
472                         }
473                 }
474                 return 0;
475         }
476
477         if (getrlimit(l->cmd, &limit) < 0)
478                 error("can't get limit: %s", strerror(errno));
479         if (set) {
480                 if (how & SOFT)
481                         limit.rlim_cur = val;
482                 if (how & HARD)
483                         limit.rlim_max = val;
484                 if (setrlimit(l->cmd, &limit) < 0)
485                         error("bad limit: %s", strerror(errno));
486         } else {
487                 if (how & SOFT)
488                         val = limit.rlim_cur;
489                 else if (how & HARD)
490                         val = limit.rlim_max;
491
492                 if (val == RLIM_INFINITY)
493                         out1fmt("unlimited\n");
494                 else
495                 {
496                         val /= l->factor;
497                         out1fmt("%jd\n", (intmax_t)val);
498                 }
499         }
500         return 0;
501 }