Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / examples / loadables / mkdir.c
1 /* mkdir - make directories */
2
3 /* See Makefile for compilation details. */
4
5 /*
6    Copyright (C) 1999-2009 Free Software Foundation, Inc.
7
8    This file is part of GNU Bash.
9    Bash is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13
14    Bash is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <config.h>
24
25 #include "bashtypes.h"
26 #include "posixstat.h"
27 #include <errno.h>
28 #include <stdio.h>
29 #include "bashansi.h"
30 #if defined (HAVE_UNISTD_H)
31 #  include <unistd.h>
32 #endif
33
34 #include "builtins.h"
35 #include "shell.h"
36 #include "bashgetopt.h"
37 #include "common.h"
38
39 #if !defined (errno)
40 extern int errno;
41 #endif
42
43 #define ISOCTAL(c)      ((c) >= '0' && (c) <= '7')
44
45 extern int parse_symbolic_mode ();
46
47 static int make_path ();
48
49 static int original_umask;
50
51 int
52 mkdir_builtin (list)
53      WORD_LIST *list;
54 {
55   int opt, pflag, omode, rval, octal, nmode, parent_mode, um;
56   char *mode;
57   WORD_LIST *l;
58
59   reset_internal_getopt ();
60   pflag = 0;
61   mode = (char *)NULL;
62   while ((opt = internal_getopt(list, "m:p")) != -1)
63     switch (opt)
64       {
65         case 'p':
66           pflag = 1;
67           break;
68         case 'm':
69           mode = list_optarg;
70           break;
71         default:
72           builtin_usage();
73           return (EX_USAGE);
74       }
75   list = loptend;
76
77   if (list == 0)
78     {
79       builtin_usage ();
80       return (EX_USAGE);
81     }
82
83   if (mode == NULL)
84     omode = S_IRWXU | S_IRWXG | S_IRWXO;        /* a=rwx */
85   else if (ISOCTAL (*mode))     /* octal number */
86     {
87       omode = read_octal (mode);
88       if (omode < 0)
89         {
90           builtin_error ("invalid file mode: %s", mode);
91           return (EXECUTION_FAILURE);
92         }
93       octal = 1;
94     }
95   else if (mode)
96     {
97       /* initial bits are a=rwx; the mode argument modifies them */
98       omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO);
99       if (omode < 0)
100         {
101           builtin_error ("invalid file mode: %s", mode);
102           return (EXECUTION_FAILURE);
103         }
104       octal = 0;
105     }
106
107   /* Make the new mode */
108   original_umask = umask (0);
109   umask (original_umask);
110
111   nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask;
112   parent_mode = nmode | (S_IWUSR|S_IXUSR);      /* u+wx */
113
114   /* Adjust new mode based on mode argument */
115   nmode &= omode;
116
117   for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
118     {
119       if (pflag && make_path (l->word->word, nmode, parent_mode))
120         {
121           rval = EXECUTION_FAILURE;
122           continue;
123         }
124       else if (pflag == 0 && mkdir (l->word->word, nmode) < 0)
125         {
126           builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno));
127           rval = EXECUTION_FAILURE;
128         }
129     }
130   return rval;
131 }
132
133 /* Make all the directories leading up to PATH, then create PATH.  Note that
134    this changes the process's umask; make sure that all paths leading to a
135    return reset it to ORIGINAL_UMASK */
136 static int
137 make_path (path, nmode, parent_mode)
138      char *path;
139      int nmode, parent_mode;
140 {
141   int oumask;
142   struct stat sb;
143   char *p, *npath;
144
145   if (stat (path, &sb) == 0)
146     {
147       if (S_ISDIR (sb.st_mode) == 0)
148         {
149           builtin_error ("`%s': file exists but is not a directory", path);
150           return 1;
151         }
152         
153       if (chmod (path, nmode))
154         {
155           builtin_error ("%s: %s", path, strerror (errno));
156           return 1;
157         }
158
159       return 0;
160     }
161
162   oumask = umask (0);
163   npath = savestring (path);    /* So we can write to it. */
164     
165   /* Check whether or not we need to do anything with intermediate dirs. */
166
167   /* Skip leading slashes. */
168   p = npath;
169   while (*p == '/')
170     p++;
171
172   while (p = strchr (p, '/'))
173     {
174       *p = '\0';
175       if (stat (npath, &sb) != 0)
176         {
177           if (mkdir (npath, parent_mode))
178             {
179               builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
180               umask (original_umask);
181               free (npath);
182               return 1;
183             }
184         }
185       else if (S_ISDIR (sb.st_mode) == 0)
186         {
187           builtin_error ("`%s': file exists but is not a directory", npath);
188           umask (original_umask);
189           free (npath);
190           return 1;
191         }
192
193       *p++ = '/';       /* restore slash */
194       while (*p == '/')
195         p++;
196     }
197
198   /* Create the final directory component. */
199   if (stat (npath, &sb) && mkdir (npath, nmode))
200     {
201       builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
202       umask (original_umask);
203       free (npath);
204       return 1;
205     }
206
207   umask (original_umask);
208   free (npath);
209   return 0;
210 }
211
212 char *mkdir_doc[] = {
213         "Create directories.",
214         "",
215         "Make directories.  Create the directories named as arguments, in",
216         "the order specified, using mode rwxrwxrwx as modified by the current",
217         "umask (see `help umask').  The -m option causes the file permission",
218         "bits of the final directory to be MODE.  The MODE argument may be",
219         "an octal number or a symbolic mode like that used by chmod(1).  If",
220         "a symbolic mode is used, the operations are interpreted relative to",
221         "an initial mode of \"a=rwx\".  The -p option causes any required",
222         "intermediate directories in PATH to be created.  The directories",
223         "are created with permssion bits of rwxrwxrwx as modified by the current",
224         "umask, plus write and search permissions for the owner.  mkdir",
225         "returns 0 if the directories are created successfully, and non-zero",
226         "if an error occurs.",
227         (char *)NULL
228 };
229
230 struct builtin mkdir_struct = {
231         "mkdir",
232         mkdir_builtin,
233         BUILTIN_ENABLED,
234         mkdir_doc,
235         "mkdir [-p] [-m mode] directory [directory ...]",
236         0
237 };