]> git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/stat.c
Bash-5.1 patch 16: fix interpretation of multiple instances of ! in [[ conditional...
[thirdparty/bash.git] / examples / loadables / stat.c
1 /* stat - load up an associative array with stat information about a file */
2
3 /* See Makefile for compilation details. */
4
5 /*
6 Copyright (C) 2016 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 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
28
29 #include <stdio.h>
30
31 #include <sys/types.h>
32 #include "posixstat.h"
33 #include <stdio.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <errno.h>
37 #include "posixtime.h"
38
39 #include "bashansi.h"
40 #include "shell.h"
41 #include "builtins.h"
42 #include "common.h"
43 #include "bashgetopt.h"
44
45 #ifndef errno
46 extern int errno;
47 #endif
48
49 #define ST_NAME 0
50 #define ST_DEV 1
51 #define ST_INO 2
52 #define ST_MODE 3
53 #define ST_NLINK 4
54 #define ST_UID 5
55 #define ST_GID 6
56 #define ST_RDEV 7
57 #define ST_SIZE 8
58 #define ST_ATIME 9
59 #define ST_MTIME 10
60 #define ST_CTIME 11
61 #define ST_BLKSIZE 12
62 #define ST_BLOCKS 13
63 #define ST_CHASELINK 14
64 #define ST_PERMS 15
65
66 #define ST_END 16
67
68 static char *arraysubs[] =
69 {
70 "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
71 "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
72 0
73 };
74
75 static int
76 getstat (fname, flags, sp)
77 const char *fname;
78 int flags;
79 struct stat *sp;
80 {
81 intmax_t lfd;
82 int fd, r;
83
84 if (strncmp (fname, "/dev/fd/", 8) == 0)
85 {
86 if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
87 {
88 errno = EINVAL;
89 return -1;
90 }
91 fd = lfd;
92 r = fstat(fd, sp);
93 }
94 #ifdef HAVE_LSTAT
95 else if (flags & 1)
96 r = lstat(fname, sp);
97 #endif
98 else
99 r = stat(fname, sp);
100
101 return r;
102 }
103
104 static char *
105 statlink (fname, sp)
106 char *fname;
107 struct stat *sp;
108 {
109 #if defined (HAVE_READLINK)
110 char linkbuf[PATH_MAX];
111 int n;
112
113 if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
114 {
115 linkbuf[n] = '\0';
116 return (savestring (linkbuf));
117 }
118 else
119 #endif
120 return (savestring (fname));
121 }
122
123 static char *
124 octalperms (m)
125 int m;
126 {
127 int operms;
128 char *ret;
129
130 operms = 0;
131
132 if (m & S_IRUSR)
133 operms |= 0400;
134 if (m & S_IWUSR)
135 operms |= 0200;
136 if (m & S_IXUSR)
137 operms |= 0100;
138
139 if (m & S_IRGRP)
140 operms |= 0040;
141 if (m & S_IWGRP)
142 operms |= 0020;
143 if (m & S_IXGRP)
144 operms |= 0010;
145
146 if (m & S_IROTH)
147 operms |= 0004;
148 if (m & S_IWOTH)
149 operms |= 0002;
150 if (m & S_IXOTH)
151 operms |= 0001;
152
153 if (m & S_ISUID)
154 operms |= 04000;
155 if (m & S_ISGID)
156 operms |= 02000;
157 if (m & S_ISVTX)
158 operms |= 01000;
159
160 ret = (char *)xmalloc (16);
161 snprintf (ret, 16, "%04o", operms);
162 return ret;
163 }
164
165 static char *
166 statperms (m)
167 int m;
168 {
169 char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
170 int i;
171 char *ret;
172
173 i = 0;
174 if (m & S_IRUSR)
175 ubits[i++] = 'r';
176 if (m & S_IWUSR)
177 ubits[i++] = 'w';
178 if (m & S_IXUSR)
179 ubits[i++] = 'x';
180 ubits[i] = '\0';
181
182 i = 0;
183 if (m & S_IRGRP)
184 gbits[i++] = 'r';
185 if (m & S_IWGRP)
186 gbits[i++] = 'w';
187 if (m & S_IXGRP)
188 gbits[i++] = 'x';
189 gbits[i] = '\0';
190
191 i = 0;
192 if (m & S_IROTH)
193 obits[i++] = 'r';
194 if (m & S_IWOTH)
195 obits[i++] = 'w';
196 if (m & S_IXOTH)
197 obits[i++] = 'x';
198 obits[i] = '\0';
199
200 if (m & S_ISUID)
201 ubits[2] = (m & S_IXUSR) ? 's' : 'S';
202 if (m & S_ISGID)
203 gbits[2] = (m & S_IXGRP) ? 's' : 'S';
204 if (m & S_ISVTX)
205 obits[2] = (m & S_IXOTH) ? 't' : 'T';
206
207 ret = (char *)xmalloc (32);
208 snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
209 return ret;
210 }
211
212 static char *
213 statmode(mode)
214 int mode;
215 {
216 char *modestr, *m;
217
218 modestr = m = (char *)xmalloc (8);
219 if (S_ISBLK (mode))
220 *m++ = 'b';
221 if (S_ISCHR (mode))
222 *m++ = 'c';
223 if (S_ISDIR (mode))
224 *m++ = 'd';
225 if (S_ISREG(mode))
226 *m++ = '-';
227 if (S_ISFIFO(mode))
228 *m++ = 'p';
229 if (S_ISLNK(mode))
230 *m++ = 'l';
231 if (S_ISSOCK(mode))
232 *m++ = 's';
233
234 #ifdef S_ISDOOR
235 if (S_ISDOOR (mode))
236 *m++ = 'D';
237 #endif
238 #ifdef S_ISWHT
239 if (S_ISWHT(mode))
240 *m++ = 'W';
241 #endif
242 #ifdef S_ISNWK
243 if (S_ISNWK(mode))
244 *m++ = 'n';
245 #endif
246 #ifdef S_ISMPC
247 if (S_ISMPC (mode))
248 *m++ = 'm';
249 #endif
250
251 *m = '\0';
252 return (modestr);
253 }
254
255 static char *
256 stattime (t)
257 time_t t;
258 {
259 char *tbuf, *ret;
260 size_t tlen;
261
262 tbuf = ctime (&t);
263 tlen = strlen (tbuf);
264 ret = savestring (tbuf);
265 ret[tlen-1] = '\0';
266 return ret;
267 }
268
269 static char *
270 statval (which, fname, flags, sp)
271 int which;
272 char *fname;
273 int flags;
274 struct stat *sp;
275 {
276 int temp;
277
278 switch (which)
279 {
280 case ST_NAME:
281 return savestring (fname);
282 case ST_DEV:
283 return itos (sp->st_dev);
284 case ST_INO:
285 return itos (sp->st_ino);
286 case ST_MODE:
287 return (statmode (sp->st_mode));
288 case ST_NLINK:
289 return itos (sp->st_nlink);
290 case ST_UID:
291 return itos (sp->st_uid);
292 case ST_GID:
293 return itos (sp->st_gid);
294 case ST_RDEV:
295 return itos (sp->st_rdev);
296 case ST_SIZE:
297 return itos (sp->st_size);
298 case ST_ATIME:
299 return ((flags & 2) ? stattime (sp->st_atime) : itos (sp->st_atime));
300 case ST_MTIME:
301 return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime));
302 case ST_CTIME:
303 return ((flags & 2) ? stattime (sp->st_ctime) : itos (sp->st_ctime));
304 case ST_BLKSIZE:
305 return itos (sp->st_blksize);
306 case ST_BLOCKS:
307 return itos (sp->st_blocks);
308 case ST_CHASELINK:
309 return (statlink (fname, sp));
310 case ST_PERMS:
311 temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
312 return (flags & 2) ? statperms (temp) : octalperms (temp);
313 default:
314 return savestring ("42");
315 }
316 }
317
318 static int
319 loadstat (vname, var, fname, flags, sp)
320 char *vname;
321 SHELL_VAR *var;
322 char *fname;
323 int flags;
324 struct stat *sp;
325 {
326 int i;
327 char *key, *value;
328 SHELL_VAR *v;
329
330 for (i = 0; arraysubs[i]; i++)
331 {
332 key = savestring (arraysubs[i]);
333 value = statval (i, fname, flags, sp);
334 v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
335 }
336 return 0;
337 }
338
339 int
340 stat_builtin (list)
341 WORD_LIST *list;
342 {
343 int opt, flags;
344 char *aname, *fname;
345 struct stat st;
346 SHELL_VAR *v;
347
348 aname = "STAT";
349 flags = 0;
350
351 reset_internal_getopt ();
352 while ((opt = internal_getopt (list, "A:Ll")) != -1)
353 {
354 switch (opt)
355 {
356 case 'A':
357 aname = list_optarg;
358 break;
359 case 'L':
360 flags |= 1; /* operate on links rather than resolving them */
361 break;
362 case 'l':
363 flags |= 2;
364 break;
365 CASE_HELPOPT;
366 default:
367 builtin_usage ();
368 return (EX_USAGE);
369 }
370 }
371
372 list = loptend;
373 if (list == 0)
374 {
375 builtin_usage ();
376 return (EX_USAGE);
377 }
378
379 fname = list->word->word;
380
381 if (getstat (fname, flags, &st) < 0)
382 {
383 builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
384 return (EXECUTION_FAILURE);
385 }
386
387 unbind_variable (aname);
388 v = make_new_assoc_variable (aname);
389 if (v == 0)
390 {
391 builtin_error ("%s: cannot create variable", aname);
392 return (EXECUTION_FAILURE);
393 }
394 if (loadstat (aname, v, fname, flags, &st) < 0)
395 {
396 builtin_error ("%s: cannot assign file status information", aname);
397 unbind_variable (aname);
398 return (EXECUTION_FAILURE);
399 }
400
401 return (EXECUTION_SUCCESS);
402 }
403
404 /* An array of strings forming the `long' documentation for a builtin xxx,
405 which is printed by `help xxx'. It must end with a NULL. By convention,
406 the first line is a short description. */
407 char *stat_doc[] = {
408 "Load an associative array with file status information.",
409 "",
410 "Take a filename and load the status information returned by a",
411 "stat(2) call on that file into the associative array specified",
412 "by the -A option. The default array name is STAT. If the -L",
413 "option is supplied, stat does not resolve symbolic links and",
414 "reports information about the link itself. The -l option results",
415 "in longer-form listings for some of the fields. The exit status is 0",
416 "unless the stat fails or assigning the array is unsuccessful.",
417 (char *)NULL
418 };
419
420 /* The standard structure describing a builtin command. bash keeps an array
421 of these structures. The flags must include BUILTIN_ENABLED so the
422 builtin can be used. */
423 struct builtin stat_struct = {
424 "stat", /* builtin name */
425 stat_builtin, /* function implementing the builtin */
426 BUILTIN_ENABLED, /* initial flags for builtin */
427 stat_doc, /* array of long documentation strings. */
428 "stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */
429 0 /* reserved for internal use */
430 };