summaryrefslogtreecommitdiffstats
path: root/examples/loadables/stat.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--examples/loadables/stat.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c
new file mode 100644
index 0000000..1e60e7b
--- /dev/null
+++ b/examples/loadables/stat.c
@@ -0,0 +1,464 @@
+/* stat - load up an associative array with stat information about a file */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 2016,2022 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#include <sys/types.h>
+#include "posixstat.h"
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include "posixtime.h"
+
+#include "bashansi.h"
+#include "shell.h"
+#include "builtins.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+#define ST_NAME 0
+#define ST_DEV 1
+#define ST_INO 2
+#define ST_MODE 3
+#define ST_NLINK 4
+#define ST_UID 5
+#define ST_GID 6
+#define ST_RDEV 7
+#define ST_SIZE 8
+#define ST_ATIME 9
+#define ST_MTIME 10
+#define ST_CTIME 11
+#define ST_BLKSIZE 12
+#define ST_BLOCKS 13
+#define ST_CHASELINK 14
+#define ST_PERMS 15
+
+#define ST_END 16
+
+static char *arraysubs[] =
+ {
+ "name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
+ "size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
+ 0
+ };
+
+#define DEFTIMEFMT "%a %b %e %k:%M:%S %Z %Y"
+#ifndef TIMELEN_MAX
+# define TIMELEN_MAX 128
+#endif
+
+static char *stattime (time_t, const char *);
+
+static int
+getstat (fname, flags, sp)
+ const char *fname;
+ int flags;
+ struct stat *sp;
+{
+ intmax_t lfd;
+ int fd, r;
+
+ if (strncmp (fname, "/dev/fd/", 8) == 0)
+ {
+ if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ fd = lfd;
+ r = fstat(fd, sp);
+ }
+#ifdef HAVE_LSTAT
+ else if (flags & 1)
+ r = lstat(fname, sp);
+#endif
+ else
+ r = stat(fname, sp);
+
+ return r;
+}
+
+static char *
+statlink (fname, sp)
+ char *fname;
+ struct stat *sp;
+{
+#if defined (HAVE_READLINK)
+ char linkbuf[PATH_MAX];
+ int n;
+
+ if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
+ {
+ linkbuf[n] = '\0';
+ return (savestring (linkbuf));
+ }
+ else
+#endif
+ return (savestring (fname));
+}
+
+static char *
+octalperms (m)
+ int m;
+{
+ int operms;
+ char *ret;
+
+ operms = 0;
+
+ if (m & S_IRUSR)
+ operms |= 0400;
+ if (m & S_IWUSR)
+ operms |= 0200;
+ if (m & S_IXUSR)
+ operms |= 0100;
+
+ if (m & S_IRGRP)
+ operms |= 0040;
+ if (m & S_IWGRP)
+ operms |= 0020;
+ if (m & S_IXGRP)
+ operms |= 0010;
+
+ if (m & S_IROTH)
+ operms |= 0004;
+ if (m & S_IWOTH)
+ operms |= 0002;
+ if (m & S_IXOTH)
+ operms |= 0001;
+
+ if (m & S_ISUID)
+ operms |= 04000;
+ if (m & S_ISGID)
+ operms |= 02000;
+ if (m & S_ISVTX)
+ operms |= 01000;
+
+ ret = (char *)xmalloc (16);
+ snprintf (ret, 16, "%04o", operms);
+ return ret;
+}
+
+static char *
+statperms (m)
+ int m;
+{
+ char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
+ int i;
+ char *ret;
+
+ i = 0;
+ if (m & S_IRUSR)
+ ubits[i++] = 'r';
+ if (m & S_IWUSR)
+ ubits[i++] = 'w';
+ if (m & S_IXUSR)
+ ubits[i++] = 'x';
+ ubits[i] = '\0';
+
+ i = 0;
+ if (m & S_IRGRP)
+ gbits[i++] = 'r';
+ if (m & S_IWGRP)
+ gbits[i++] = 'w';
+ if (m & S_IXGRP)
+ gbits[i++] = 'x';
+ gbits[i] = '\0';
+
+ i = 0;
+ if (m & S_IROTH)
+ obits[i++] = 'r';
+ if (m & S_IWOTH)
+ obits[i++] = 'w';
+ if (m & S_IXOTH)
+ obits[i++] = 'x';
+ obits[i] = '\0';
+
+ if (m & S_ISUID)
+ ubits[2] = (m & S_IXUSR) ? 's' : 'S';
+ if (m & S_ISGID)
+ gbits[2] = (m & S_IXGRP) ? 's' : 'S';
+ if (m & S_ISVTX)
+ obits[2] = (m & S_IXOTH) ? 't' : 'T';
+
+ ret = (char *)xmalloc (32);
+ snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
+ return ret;
+}
+
+static char *
+statmode(mode)
+ int mode;
+{
+ char *modestr, *m;
+
+ modestr = m = (char *)xmalloc (8);
+ if (S_ISBLK (mode))
+ *m++ = 'b';
+ if (S_ISCHR (mode))
+ *m++ = 'c';
+ if (S_ISDIR (mode))
+ *m++ = 'd';
+ if (S_ISREG(mode))
+ *m++ = '-';
+ if (S_ISFIFO(mode))
+ *m++ = 'p';
+ if (S_ISLNK(mode))
+ *m++ = 'l';
+ if (S_ISSOCK(mode))
+ *m++ = 's';
+
+#ifdef S_ISDOOR
+ if (S_ISDOOR (mode))
+ *m++ = 'D';
+#endif
+#ifdef S_ISWHT
+ if (S_ISWHT(mode))
+ *m++ = 'W';
+#endif
+#ifdef S_ISNWK
+ if (S_ISNWK(mode))
+ *m++ = 'n';
+#endif
+#ifdef S_ISMPC
+ if (S_ISMPC (mode))
+ *m++ = 'm';
+#endif
+
+ *m = '\0';
+ return (modestr);
+}
+
+static char *
+stattime (t, timefmt)
+ time_t t;
+ const char *timefmt;
+{
+ char *tbuf, *ret;
+ const char *fmt;
+ size_t tlen;
+ struct tm *tm;
+
+ fmt = timefmt ? timefmt : DEFTIMEFMT;
+ tm = localtime (&t);
+
+ ret = xmalloc (TIMELEN_MAX);
+
+ tlen = strftime (ret, TIMELEN_MAX, fmt, tm);
+ if (tlen == 0)
+ tlen = strftime (ret, TIMELEN_MAX, DEFTIMEFMT, tm);
+
+ return ret;
+}
+
+static char *
+statval (which, fname, flags, fmt, sp)
+ int which;
+ char *fname;
+ int flags;
+ char *fmt;
+ struct stat *sp;
+{
+ int temp;
+
+ switch (which)
+ {
+ case ST_NAME:
+ return savestring (fname);
+ case ST_DEV:
+ return itos (sp->st_dev);
+ case ST_INO:
+ return itos (sp->st_ino);
+ case ST_MODE:
+ return (statmode (sp->st_mode));
+ case ST_NLINK:
+ return itos (sp->st_nlink);
+ case ST_UID:
+ return itos (sp->st_uid);
+ case ST_GID:
+ return itos (sp->st_gid);
+ case ST_RDEV:
+ return itos (sp->st_rdev);
+ case ST_SIZE:
+ return itos (sp->st_size);
+ case ST_ATIME:
+ return ((flags & 2) ? stattime (sp->st_atime, fmt) : itos (sp->st_atime));
+ case ST_MTIME:
+ return ((flags & 2) ? stattime (sp->st_mtime, fmt) : itos (sp->st_mtime));
+ case ST_CTIME:
+ return ((flags & 2) ? stattime (sp->st_ctime, fmt) : itos (sp->st_ctime));
+ case ST_BLKSIZE:
+ return itos (sp->st_blksize);
+ case ST_BLOCKS:
+ return itos (sp->st_blocks);
+ case ST_CHASELINK:
+ return (statlink (fname, sp));
+ case ST_PERMS:
+ temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
+ return (flags & 2) ? statperms (temp) : octalperms (temp);
+ default:
+ return savestring ("42");
+ }
+}
+
+static int
+loadstat (vname, var, fname, flags, fmt, sp)
+ char *vname;
+ SHELL_VAR *var;
+ char *fname;
+ int flags;
+ char *fmt;
+ struct stat *sp;
+{
+ int i;
+ char *key, *value;
+ SHELL_VAR *v;
+
+ for (i = 0; arraysubs[i]; i++)
+ {
+ key = savestring (arraysubs[i]);
+ value = statval (i, fname, flags, fmt, sp);
+ v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
+ }
+ return 0;
+}
+
+int
+stat_builtin (list)
+ WORD_LIST *list;
+{
+ int opt, flags;
+ char *aname, *fname, *timefmt;
+ struct stat st;
+ SHELL_VAR *v;
+
+ aname = "STAT";
+ flags = 0;
+ timefmt = 0;
+
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "A:F:Ll")) != -1)
+ {
+ switch (opt)
+ {
+ case 'A':
+ aname = list_optarg;
+ break;
+ case 'L':
+ flags |= 1; /* operate on links rather than resolving them */
+ break;
+ case 'l':
+ flags |= 2;
+ break;
+ case 'F':
+ timefmt = list_optarg;
+ break;
+ CASE_HELPOPT;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+ if (legal_identifier (aname) == 0)
+ {
+ sh_invalidid (aname);
+ return (EXECUTION_FAILURE);
+ }
+
+ list = loptend;
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+
+#if 0
+ unbind_variable (aname);
+#endif
+ fname = list->word->word;
+
+ if (getstat (fname, flags, &st) < 0)
+ {
+ builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+
+ v = find_or_make_array_variable (aname, 3);
+ if (v == 0)
+ {
+ builtin_error ("%s: cannot create variable", aname);
+ return (EXECUTION_FAILURE);
+ }
+ if (loadstat (aname, v, fname, flags, timefmt, &st) < 0)
+ {
+ builtin_error ("%s: cannot assign file status information", aname);
+ unbind_variable (aname);
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+/* An array of strings forming the `long' documentation for a builtin xxx,
+ which is printed by `help xxx'. It must end with a NULL. By convention,
+ the first line is a short description. */
+char *stat_doc[] = {
+ "Load an associative array with file status information.",
+ "",
+ "Take a filename and load the status information returned by a",
+ "stat(2) call on that file into the associative array specified",
+ "by the -A option. The default array name is STAT.",
+ "",
+ "If the -L option is supplied, stat does not resolve symbolic links",
+ "and reports information about the link itself. The -l option results",
+ "in longer-form listings for some of the fields. When -l is used,",
+ "the -F option supplies a format string passed to strftime(3) to",
+ "display the file time information.",
+ "The exit status is 0 unless the stat fails or assigning the array",
+ "is unsuccessful.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. The flags must include BUILTIN_ENABLED so the
+ builtin can be used. */
+struct builtin stat_struct = {
+ "stat", /* builtin name */
+ stat_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ stat_doc, /* array of long documentation strings. */
+ "stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};