mirror of https://github.com/n-hys/bash.git
431 lines
8.9 KiB
C
431 lines
8.9 KiB
C
/* stat - load up an associative array with stat information about a file */
|
|
|
|
/* See Makefile for compilation details. */
|
|
|
|
/*
|
|
Copyright (C) 2016 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
|
|
};
|
|
|
|
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)
|
|
time_t t;
|
|
{
|
|
char *tbuf, *ret;
|
|
size_t tlen;
|
|
|
|
tbuf = ctime (&t);
|
|
tlen = strlen (tbuf);
|
|
ret = savestring (tbuf);
|
|
ret[tlen-1] = '\0';
|
|
return ret;
|
|
}
|
|
|
|
static char *
|
|
statval (which, fname, flags, sp)
|
|
int which;
|
|
char *fname;
|
|
int flags;
|
|
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) : itos (sp->st_atime));
|
|
case ST_MTIME:
|
|
return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime));
|
|
case ST_CTIME:
|
|
return ((flags & 2) ? stattime (sp->st_ctime) : 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, sp)
|
|
char *vname;
|
|
SHELL_VAR *var;
|
|
char *fname;
|
|
int flags;
|
|
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, 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;
|
|
struct stat st;
|
|
SHELL_VAR *v;
|
|
|
|
aname = "STAT";
|
|
flags = 0;
|
|
|
|
reset_internal_getopt ();
|
|
while ((opt = internal_getopt (list, "A: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_HELPOPT;
|
|
default:
|
|
builtin_usage ();
|
|
return (EX_USAGE);
|
|
}
|
|
}
|
|
|
|
list = loptend;
|
|
if (list == 0)
|
|
{
|
|
builtin_usage ();
|
|
return (EX_USAGE);
|
|
}
|
|
|
|
fname = list->word->word;
|
|
|
|
if (getstat (fname, flags, &st) < 0)
|
|
{
|
|
builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
|
|
return (EXECUTION_FAILURE);
|
|
}
|
|
|
|
unbind_variable (aname);
|
|
v = make_new_assoc_variable (aname);
|
|
if (v == 0)
|
|
{
|
|
builtin_error ("%s: cannot create variable", aname);
|
|
return (EXECUTION_FAILURE);
|
|
}
|
|
if (loadstat (aname, v, fname, flags, &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. 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 */
|
|
};
|