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