diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/modules/stat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openenvutils/commandshell/shell/src/modules/stat.c Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,650 @@ +// stat.c - stat builtin interface to system call +// +// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. +// +/* + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1996-1997 Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Peter Stephenson or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Peter Stephenson and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Peter Stephenson and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Peter Stephenson and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ +#include "stat.mdh" +#include "stat.pro" + +#ifdef __SYMBIAN32__ +#ifdef __WINSCW__ +#pragma warn_unusedarg off +#endif//__WINSCW__ +#endif//__SYMBIAN32__ + +enum statnum { ST_DEV, ST_INO, ST_MODE, ST_NLINK, ST_UID, ST_GID, + ST_RDEV, ST_SIZE, ST_ATIM, ST_MTIM, ST_CTIM, + ST_BLKSIZE, ST_BLOCKS, ST_READLINK, ST_COUNT }; +enum statflags { STF_NAME = 1, STF_FILE = 2, STF_STRING = 4, STF_RAW = 8, + STF_PICK = 16, STF_ARRAY = 32, STF_GMT = 64, + STF_HASH = 128, STF_OCTAL = 256 }; +static char *statelts[] = { "device", "inode", "mode", "nlink", + "uid", "gid", "rdev", "size", "atime", + "mtime", "ctime", "blksize", "blocks", + "link", NULL }; +#define HNAMEKEY "name" + +/**/ +static void +statmodeprint(mode_t mode, char *outbuf, int flags) +{ + if (flags & STF_RAW) { + sprintf(outbuf, (flags & STF_OCTAL) ? "0%lo" : "%lu", + (unsigned long)mode); + if (flags & STF_STRING) + strcat(outbuf, " ("); + } + if (flags & STF_STRING) { + static const char *modes = "?rwxrwxrwx"; +#ifdef __CYGWIN__ + static mode_t mflags[9] = { 0 }; +#else + static const mode_t mflags[9] = { + S_IRUSR, S_IWUSR, S_IXUSR, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IROTH, S_IWOTH, S_IXOTH + }; +#endif + const mode_t *mfp = mflags; + char pm[11]; + int i; + +#ifdef __CYGWIN__ + if (mflags[0] == 0) { + mflags[0] = S_IRUSR; + mflags[1] = S_IWUSR; + mflags[2] = S_IXUSR; + mflags[3] = S_IRGRP; + mflags[4] = S_IWGRP; + mflags[5] = S_IXGRP; + mflags[6] = S_IROTH; + mflags[7] = S_IWOTH; + mflags[8] = S_IXOTH; + } +#endif + + if (S_ISBLK(mode)) + *pm = 'b'; + else if (S_ISCHR(mode)) + *pm = 'c'; + else if (S_ISDIR(mode)) + *pm = 'd'; + else if (S_ISDOOR(mode)) + *pm = 'D'; + else if (S_ISFIFO(mode)) + *pm = 'p'; + else if (S_ISLNK(mode)) + *pm = 'l'; + else if (S_ISMPC(mode)) + *pm = 'm'; + else if (S_ISNWK(mode)) + *pm = 'n'; + else if (S_ISOFD(mode)) + *pm = 'M'; + else if (S_ISOFL(mode)) + *pm = 'M'; + else if (S_ISREG(mode)) + *pm = '-'; + else if (S_ISSOCK(mode)) + *pm = 's'; + else + *pm = '?'; + + for (i = 1; i <= 9; i++) + pm[i] = (mode & *mfp++) ? modes[i] : '-'; + pm[10] = '\0'; + + if (mode & S_ISUID) + pm[3] = (mode & S_IXUSR) ? 's' : 'S'; + if (mode & S_ISGID) + pm[6] = (mode & S_IXGRP) ? 's' : 'S'; + if (mode & S_ISVTX) + pm[9] = (mode & S_IXOTH) ? 't' : 'T'; + + pm[10] = 0; + strcat(outbuf, pm); + if (flags & STF_RAW) + strcat(outbuf, ")"); + } +} + + +/**/ +static void +statuidprint(uid_t uid, char *outbuf, int flags) +{ + if (flags & STF_RAW) { + sprintf(outbuf, "%lu", (unsigned long)uid); + if (flags & STF_STRING) + strcat(outbuf, " ("); + } + if (flags & STF_STRING) { +#ifdef HAVE_GETPWUID + struct passwd *pwd; + pwd = getpwuid(uid); + strcat(outbuf, pwd ? pwd->pw_name : "???"); +#else /* !HAVE_GETPWUID */ + strcat(outbuf, "???"); +#endif /* !HAVE_GETPWUID */ + if (flags & STF_RAW) + strcat(outbuf, ")"); + } +} + + +/**/ +static void +statgidprint(gid_t gid, char *outbuf, int flags) +{ + if (flags & STF_RAW) { + sprintf(outbuf, "%lu", (unsigned long)gid); + if (flags & STF_STRING) + strcat(outbuf, " ("); + } + if (flags & STF_STRING) { +#ifdef HAVE_GETGRGID + struct group *gr; + gr = getgrgid(gid); + strcat(outbuf, gr ? gr->gr_name : "???"); +#else /* !HAVE_GETGRGID */ + strcat(outbuf, "???"); +#endif /* !HAVE_GETGRGID */ + if (flags & STF_RAW) + strcat(outbuf, ")"); + } +} + +static char *timefmt; + +/**/ +static void +stattimeprint(time_t tim, char *outbuf, int flags) +{ + if (flags & STF_RAW) { + sprintf(outbuf, "%ld", (unsigned long)tim); + if (flags & STF_STRING) + strcat(outbuf, " ("); + } + if (flags & STF_STRING) { + char *oend = outbuf + strlen(outbuf); + ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) : + localtime(&tim)); + if (flags & STF_RAW) + strcat(outbuf, ")"); + } +} + + +/**/ +static void +statulprint(unsigned long num, char *outbuf) +{ + sprintf(outbuf, "%lu", num); +} + + +/**/ +static void +statlinkprint(struct stat *sbuf, char *outbuf, char *fname) +{ + int num; + + /* fname is NULL if we are looking at an fd */ + if (fname && S_ISLNK(sbuf->st_mode) && + (num = readlink(fname, outbuf, PATH_MAX)) > 0) { + /* readlink doesn't terminate the buffer itself */ + outbuf[num] = '\0'; + } +} + + +/**/ +static void +statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags) +{ + char *optr = outbuf; + + if (flags & STF_NAME) { + sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ? + "%s " : "%-8s", statelts[iwhich]); + optr += strlen(outbuf); + } + *optr = '\0'; + + /* cast values to unsigned long as safest bet */ + switch (iwhich) { + case ST_DEV: + statulprint((unsigned long)sbuf->st_dev, optr); + break; + + case ST_INO: +#ifdef INO_T_IS_64_BIT + convbase(optr, sbuf->st_ino, 0); +#else + DPUTS(sizeof(sbuf->st_ino) > 4, + "Shell compiled with wrong ino_t size"); + statulprint((unsigned long)sbuf->st_ino, optr); +#endif + break; + + case ST_MODE: + statmodeprint(sbuf->st_mode, optr, flags); + break; + + case ST_NLINK: + statulprint((unsigned long)sbuf->st_nlink, optr); + break; + + case ST_UID: + statuidprint(sbuf->st_uid, optr, flags); + break; + + case ST_GID: + statgidprint(sbuf->st_gid, optr, flags); + break; + + case ST_RDEV: + statulprint((unsigned long)sbuf->st_rdev, optr); + break; + + case ST_SIZE: +#ifdef OFF_T_IS_64_BIT + convbase(optr, sbuf->st_size, 0); +#else + DPUTS(sizeof(sbuf->st_size) > 4, + "Shell compiled with wrong off_t size"); + statulprint((unsigned long)sbuf->st_size, optr); +#endif + break; + + case ST_ATIM: + stattimeprint(sbuf->st_atime, optr, flags); + break; + + case ST_MTIM: + stattimeprint(sbuf->st_mtime, optr, flags); + break; + + case ST_CTIM: + stattimeprint(sbuf->st_ctime, optr, flags); + break; + + case ST_BLKSIZE: + statulprint((unsigned long)sbuf->st_blksize, optr); + break; + + case ST_BLOCKS: + statulprint((unsigned long)sbuf->st_blocks, optr); + break; + + case ST_READLINK: + statlinkprint(sbuf, optr, fname); + break; + + case ST_COUNT: /* keep some compilers happy */ + break; + } +} + + +/* + * + * Options: + * -f fd: stat fd instead of file + * -g: use GMT rather than local time for time strings (forces -s on). + * -n: always print file name of file being statted + * -N: never print file name + * -l: list stat types + * -L: do lstat (else links are implicitly dereferenced by stat) + * -t: always print name of stat type + * -T: never print name of stat type + * -r: print raw alongside string data + * -s: string, print mode, times, uid, gid as appropriate strings: + * harmless but unnecessary when combined with -r. + * -A array: assign results to given array, one stat result per element. + * File names and type names are only added if explicitly requested: + * file names are returned as a separate array element, type names as + * prefix to element. Note the formatting deliberately contains + * fewer frills when -A is used. + * -H hash: as for -A array, but returns a hash with the keys being those + * from stat -l + * -F fmt: specify a $TIME-like format for printing times; the default + * is the (CTIME-like) "%a %b %e %k:%M:%S". This option implies + * -s as it is not useful for numerical times. + * + * +type selects just element type of stat buffer (-l gives list): + * type can be shortened to unambiguous string. only one type is + * allowed. The extra type, +link, reads the target of a symbolic + * link; it is empty if the stat was not an lstat or if + * a file descriptor was stat'd, if the stat'd file is + * not a symbolic link, or if symbolic links are not + * supported. If +link is explicitly requested, the -L (lstat) + * option is set automatically. + */ +/**/ +static int +bin_stat(char *name, char **args, Options ops, UNUSED(int func)) +{ + char **aptr, *arrnam = NULL, **array = NULL, **arrptr = NULL; + char *hashnam = NULL, **hash = NULL, **hashptr = NULL; + int len, iwhich = -1, ret = 0, flags = 0, arrsize = 0, fd = 0; + struct stat statbuf; + int found = 0, nargs; + + timefmt = "%a %b %e %k:%M:%S"; + + for (; *args && (**args == '+' || **args == '-'); args++) { + char *arg = *args+1; + if (!*arg || *arg == '-' || *arg == '+') { + args++; + break; + } + + if (**args == '+') { + if (found) + break; + len = strlen(arg); + for (aptr = statelts; *aptr; aptr++) + if (!strncmp(*aptr, arg, len)) { + found++; + iwhich = aptr - statelts; + } + if (found > 1) { + zwarnnam(name, "%s: ambiguous stat element", arg, 0); + return 1; + } else if (found == 0) { + zwarnnam(name, "%s: no such stat element", arg, 0); + return 1; + } + /* if name of link requested, turn on lstat */ + if (iwhich == ST_READLINK) + ops->ind['L'] = 1; + flags |= STF_PICK; + } else { + for (; *arg; arg++) { + if (strchr("glLnNorstT", *arg)) + ops->ind[STOUC(*arg)] = 1; + else if (*arg == 'A') { + if (arg[1]) { + arrnam = arg+1; + } else if (!(arrnam = *++args)) { + zwarnnam(name, "missing parameter name", + NULL, 0); + return 1; + } + flags |= STF_ARRAY; + break; + } else if (*arg == 'H') { + if (arg[1]) { + hashnam = arg+1; + } else if (!(hashnam = *++args)) { + zwarnnam(name, "missing parameter name", + NULL, 0); + return 1; + } + flags |= STF_HASH; + break; + } else if (*arg == 'f') { + char *sfd; + ops->ind['f'] = 1; + if (arg[1]) { + sfd = arg+1; + } else if (!(sfd = *++args)) { + zwarnnam(name, "missing file descriptor", NULL, 0); + return 1; + } + fd = zstrtol(sfd, &sfd, 10); + if (*sfd) { + zwarnnam(name, "bad file descriptor", NULL, 0); + return 1; + } + break; + } else if (*arg == 'F') { + if (arg[1]) { + timefmt = arg+1; + } else if (!(timefmt = *++args)) { + zwarnnam(name, "missing time format", NULL, 0); + return 1; + } + /* force string format in order to use time format */ + ops->ind['s'] = 1; + break; + } else { + zwarnnam(name, "bad option: -%c", NULL, *arg); + return 1; + } + } + } + } + + if ((flags & STF_ARRAY) && (flags & STF_HASH)) { + /* We don't implement setting multiple variables at once */ + zwarnnam(name, "both array and hash requested", NULL, 0); + return 1; + /* Alternate method would be to make -H blank arrnam etc etc * + * and so get 'silent loss' of earlier choice, which would * + * be similar to stat -A foo -A bar filename */ + } + + if (OPT_ISSET(ops,'l')) { + /* list types and return: can also list to array */ + if (arrnam) { + arrptr = array = (char **)zalloc((ST_COUNT+1)*sizeof(char *)); + array[ST_COUNT] = NULL; + } + for (aptr = statelts; *aptr; aptr++) { + if (arrnam) { + *arrptr++ = ztrdup(*aptr); + } else { + printf("%s", *aptr); + if (aptr[1]) + putchar(' '); + } + } + if (arrnam) { + setaparam(arrnam, array); + if (errflag) + return 1; + } else + putchar('\n'); + return 0; + } + + if (!*args && !OPT_ISSET(ops,'f')) { + zwarnnam(name, "no files given", NULL, 0); + return 1; + } else if (*args && OPT_ISSET(ops,'f')) { + zwarnnam(name, "no files allowed with -f", NULL, 0); + return 1; + } + + nargs = 0; + if (OPT_ISSET(ops,'f')) + nargs = 1; + else + for (aptr = args; *aptr; aptr++) + nargs++; + + if (OPT_ISSET(ops,'g')) { + flags |= STF_GMT; + ops->ind['s'] = 1; + } + if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'r')) + flags |= STF_STRING; + if (OPT_ISSET(ops,'r') || !OPT_ISSET(ops,'s')) + flags |= STF_RAW; + if (OPT_ISSET(ops,'n')) + flags |= STF_FILE; + if (OPT_ISSET(ops,'o')) + flags |= STF_OCTAL; + if (OPT_ISSET(ops,'t')) + flags |= STF_NAME; + + if (!(arrnam || hashnam)) { + if (nargs > 1) + flags |= STF_FILE; + if (!(flags & STF_PICK)) + flags |= STF_NAME; + } + + if (OPT_ISSET(ops,'N') || OPT_ISSET(ops,'f')) + flags &= ~STF_FILE; + if (OPT_ISSET(ops,'T') || OPT_ISSET(ops,'H')) + flags &= ~STF_NAME; + + if (hashnam) { + if (nargs > 1) { + zwarnnam(name, "only one file allowed with -H", NULL, 0); + return 1; + } + arrsize = (flags & STF_PICK) ? 1 : ST_COUNT; + if (flags & STF_FILE) + arrsize++; + hashptr = hash = (char **)zshcalloc((arrsize+1)*2*sizeof(char *)); + } + + if (arrnam) { + arrsize = (flags & STF_PICK) ? 1 : ST_COUNT; + if (flags & STF_FILE) + arrsize++; + arrsize *= nargs; + arrptr = array = (char **)zshcalloc((arrsize+1)*sizeof(char *)); + } + + for (; OPT_ISSET(ops,'f') || *args; args++) { + char outbuf[PATH_MAX + 9]; /* "link " + link name + NULL */ + int rval = OPT_ISSET(ops,'f') ? fstat(fd, &statbuf) : + OPT_ISSET(ops,'L') ? lstat(*args, &statbuf) : + stat(*args, &statbuf); + if (rval) { + if (OPT_ISSET(ops,'f')) + sprintf(outbuf, "%d", fd); + zwarnnam(name, "%s: %e", OPT_ISSET(ops,'f') ? outbuf : *args, + errno); + ret = 1; + if (OPT_ISSET(ops,'f') || arrnam) + break; + else + continue; + } + + if (flags & STF_FILE) { + if (arrnam) + *arrptr++ = ztrdup(*args); + else if (hashnam) { + *hashptr++ = ztrdup(HNAMEKEY); + *hashptr++ = ztrdup(*args); + } else + printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n"); + } + if (iwhich > -1) { + statprint(&statbuf, outbuf, *args, iwhich, flags); + if (arrnam) + *arrptr++ = ztrdup(outbuf); + else if (hashnam) { + /* STF_NAME explicitly turned off for ops.ind['H'] above */ + *hashptr++ = ztrdup(statelts[iwhich]); + *hashptr++ = ztrdup(outbuf); + } else + printf("%s\n", outbuf); + } else { + int i; + for (i = 0; i < ST_COUNT; i++) { + statprint(&statbuf, outbuf, *args, i, flags); + if (arrnam) + *arrptr++= ztrdup(outbuf); + else if (hashnam) { + /* STF_NAME explicitly turned off for ops.ind['H'] above */ + *hashptr++ = ztrdup(statelts[i]); + *hashptr++ = ztrdup(outbuf); + } else + printf("%s\n", outbuf); + } + } + if (OPT_ISSET(ops,'f')) + break; + + if (!arrnam && !hashnam && args[1] && !(flags & STF_PICK)) + putchar('\n'); + } + + if (arrnam) { + if (ret) + freearray(array); + else { + setaparam(arrnam, array); + if (errflag) + return 1; + } + } + + if (hashnam) { + if (ret) + freearray(hash); + else { + sethparam(hashnam, hash); + if (errflag) + return 1; + } + } + + return ret; +} + +static struct builtin bintab[] = { + BUILTIN("stat", 0, bin_stat, 0, -1, 0, NULL, NULL), +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +boot_(Module m) +{ + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + +/**/ +int +cleanup_(Module m) +{ + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +}