1This file is umask.def, from which is created umask.c.
2It implements the builtin "umask" in Bash.
3
4Copyright (C) 1987-2004 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING.  If not, write to the Free Software
20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
21
22$PRODUCES umask.c
23
24$BUILTIN umask
25$FUNCTION umask_builtin
26$SHORT_DOC umask [-p] [-S] [mode]
27The user file-creation mask is set to MODE.  If MODE is omitted, or if
28`-S' is supplied, the current value of the mask is printed.  The `-S'
29option makes the output symbolic; otherwise an octal number is output.
30If `-p' is supplied, and MODE is omitted, the output is in a form
31that may be used as input.  If MODE begins with a digit, it is
32interpreted as an octal number, otherwise it is a symbolic mode string
33like that accepted by chmod(1).
34$END
35
36#include <config.h>
37
38#include "../bashtypes.h"
39#include "filecntl.h"
40#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
41#  include <sys/file.h>
42#endif
43
44#if defined (HAVE_UNISTD_H)
45#include <unistd.h>
46#endif
47
48#include <stdio.h>
49#include <chartypes.h>
50
51#include "../bashintl.h"
52
53#include "../shell.h"
54#include "posixstat.h"
55#include "common.h"
56#include "bashgetopt.h"
57
58#ifdef __LCC__
59#define mode_t int
60#endif
61
62/* **************************************************************** */
63/*                                                                  */
64/*                     UMASK Builtin and Helpers                    */
65/*                                                                  */
66/* **************************************************************** */
67
68static void print_symbolic_umask __P((mode_t));
69static int symbolic_umask __P((WORD_LIST *));
70
71/* Set or display the mask used by the system when creating files.  Flag
72   of -S means display the umask in a symbolic mode. */
73int
74umask_builtin (list)
75     WORD_LIST *list;
76{
77  int print_symbolically, opt, umask_value, pflag;
78  mode_t umask_arg;
79
80  print_symbolically = pflag = 0;
81  reset_internal_getopt ();
82  while ((opt = internal_getopt (list, "Sp")) != -1)
83    {
84      switch (opt)
85	{
86	case 'S':
87	  print_symbolically++;
88	  break;
89	case 'p':
90	  pflag++;
91	  break;
92	default:
93	  builtin_usage ();
94	  return (EX_USAGE);
95	}
96    }
97
98  list = loptend;
99
100  if (list)
101    {
102      if (DIGIT (*list->word->word))
103	{
104	  umask_value = read_octal (list->word->word);
105
106	  /* Note that other shells just let you set the umask to zero
107	     by specifying a number out of range.  This is a problem
108	     with those shells.  We don't change the umask if the input
109	     is lousy. */
110	  if (umask_value == -1)
111	    {
112	      sh_erange (list->word->word, _("octal number"));
113	      return (EXECUTION_FAILURE);
114	    }
115	}
116      else
117	{
118	  umask_value = symbolic_umask (list);
119	  if (umask_value == -1)
120	    return (EXECUTION_FAILURE);
121	}
122      umask_arg = (mode_t)umask_value;
123      umask (umask_arg);
124      if (print_symbolically)
125	print_symbolic_umask (umask_arg);
126    }
127  else				/* Display the UMASK for this user. */
128    {
129      umask_arg = umask (022);
130      umask (umask_arg);
131
132      if (pflag)
133	printf ("umask%s ", (print_symbolically ? " -S" : ""));
134      if (print_symbolically)
135	print_symbolic_umask (umask_arg);
136      else
137	printf ("%04lo\n", (unsigned long)umask_arg);
138    }
139
140  fflush (stdout);
141  return (EXECUTION_SUCCESS);
142}
143
144/* Print the umask in a symbolic form.  In the output, a letter is
145   printed if the corresponding bit is clear in the umask. */
146static void
147print_symbolic_umask (um)
148     mode_t um;
149{
150  char ubits[4], gbits[4], obits[4];		/* u=rwx,g=rwx,o=rwx */
151  int i;
152
153  i = 0;
154  if ((um & S_IRUSR) == 0)
155    ubits[i++] = 'r';
156  if ((um & S_IWUSR) == 0)
157    ubits[i++] = 'w';
158  if ((um & S_IXUSR) == 0)
159    ubits[i++] = 'x';
160  ubits[i] = '\0';
161
162  i = 0;
163  if ((um & S_IRGRP) == 0)
164    gbits[i++] = 'r';
165  if ((um & S_IWGRP) == 0)
166    gbits[i++] = 'w';
167  if ((um & S_IXGRP) == 0)
168    gbits[i++] = 'x';
169  gbits[i] = '\0';
170
171  i = 0;
172  if ((um & S_IROTH) == 0)
173    obits[i++] = 'r';
174  if ((um & S_IWOTH) == 0)
175    obits[i++] = 'w';
176  if ((um & S_IXOTH) == 0)
177    obits[i++] = 'x';
178  obits[i] = '\0';
179
180  printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits);
181}
182
183int
184parse_symbolic_mode (mode, initial_bits)
185     char *mode;
186     int initial_bits;
187{
188  int who, op, perm, bits, c;
189  char *s;
190
191  for (s = mode, bits = initial_bits;;)
192    {
193      who = op = perm = 0;
194
195      /* Parse the `who' portion of the symbolic mode clause. */
196      while (member (*s, "agou"))
197	{
198	  switch (c = *s++)
199	    {
200	    case 'u':
201	      who |= S_IRWXU;
202	      continue;
203	    case 'g':
204	      who |= S_IRWXG;
205	      continue;
206	    case 'o':
207	      who |= S_IRWXO;
208	      continue;
209	    case 'a':
210	      who |= S_IRWXU | S_IRWXG | S_IRWXO;
211	      continue;
212	    default:
213	      break;
214	    }
215	}
216
217      /* The operation is now sitting in *s. */
218      op = *s++;
219      switch (op)
220	{
221	case '+':
222	case '-':
223	case '=':
224	  break;
225	default:
226	  builtin_error (_("`%c': invalid symbolic mode operator"), op);
227	  return (-1);
228	}
229
230      /* Parse out the `perm' section of the symbolic mode clause. */
231      while (member (*s, "rwx"))
232	{
233	  c = *s++;
234
235	  switch (c)
236	    {
237	    case 'r':
238	      perm |= S_IRUGO;
239	      break;
240	    case 'w':
241	      perm |= S_IWUGO;
242	      break;
243	    case 'x':
244	      perm |= S_IXUGO;
245	      break;
246	    }
247	}
248
249      /* Now perform the operation or return an error for a
250	 bad permission string. */
251      if (!*s || *s == ',')
252	{
253	  if (who)
254	    perm &= who;
255
256	  switch (op)
257	    {
258	    case '+':
259	      bits |= perm;
260	      break;
261	    case '-':
262	      bits &= ~perm;
263	      break;
264	    case '=':
265	      if (who == 0)
266		who = S_IRWXU | S_IRWXG | S_IRWXO;
267	      bits &= ~who;
268	      bits |= perm;
269	      break;
270
271	    /* No other values are possible. */
272	    }
273
274	  if (*s == '\0')
275	    break;
276	  else
277	    s++;	/* skip past ',' */
278	}
279      else
280	{
281	  builtin_error (_("`%c': invalid symbolic mode character"), *s);
282	  return (-1);
283	}
284    }
285
286  return (bits);
287}
288
289/* Set the umask from a symbolic mode string similar to that accepted
290   by chmod.  If the -S argument is given, then print the umask in a
291   symbolic form. */
292static int
293symbolic_umask (list)
294     WORD_LIST *list;
295{
296  int um, bits;
297
298  /* Get the initial umask.  Don't change it yet. */
299  um = umask (022);
300  umask (um);
301
302  /* All work is done with the complement of the umask -- it's
303     more intuitive and easier to deal with.  It is complemented
304     again before being returned. */
305  bits = parse_symbolic_mode (list->word->word, ~um & 0777);
306  if (bits == -1)
307    return (-1);
308
309  um = ~bits & 0777;
310  return (um);
311}
312