/******************************************************************************* * c0xfstat.c * Test implementation for the ISO C proposal at: * . * * This code is designed to run reasonably well on Unix and Win32 systems. * * Contains * _fgetfileinfo() * _getfileinfo() * * Notes * Compile with macro '_unix' defined for Unix systems. * * Compile with macro 'NOTEST' defined if you do not want a test driver * 'main()' function compiled into the object code. * * Acknowledgments * This code was written by David R. Tribble, Oct 2003. * It is hereby placed into the public domain, and may be used for all * purposes, both commercial and private. No warranty is provided for this * source code, and the author cannot be held liable for its use in any * way. * * See also * * * */ /* Identification */ static const char REV[] = "@(#)drt/text/stdc/c0xfstat.c $Revision: 1.5 $ $Date: 2003/12/30 20:26:26 $\n"; static const char PROG[] = "c0xfstat"; /* System includes */ #include #include #include #include #include #include #include /* Local includes */ #include "c0xfstat.h" /* Local macros */ #ifdef NOTEST #undef NOTEST #define NOTEST 1 #else #define NOTEST 0 #endif #define maxt(a,b) ((a) > (b) ? (a) : (b)) /* Local constants */ /* printf() format specifiers */ #if defined(_WIN32) && _MSC_VER < 1300 /* For older versions of MS C */ #define LLONG_FMT "I64d" #define ULLONG_FMT "I64u" #else #define LLONG_FMT "lld" #define ULLONG_FMT "llu" #endif /*------------------------------------------------------------------------------ * convert_info() * Convert 'struct stat' file information into 'struct _fileinfo' form. * * @param st * Pointer to a file information structure. * * @param info * Pointer to a file information structure. * * @return * Zero if successful and the structure pointed to by 'info' is filled with * information about the file; otherwise -1. * * @since 1.1, 2003-10-07 */ static int convert_info(const struct stat *st, struct _fileinfo *info) { #if _FILEINFO_VERS != 2 #error Struct _fileinfo has changed #endif long int v; /* Reset the info */ memset(info, 0, sizeof(*info)); info->fi_vers = _FILEINFO_VERS; /* Convert the file type */ if ((st->st_mode & S_IFMT) == S_IFREG) info->fi_type = _FILE_TYPE_FILE; else if ((st->st_mode & S_IFMT) == S_IFDIR) info->fi_type = _FILE_TYPE_DIR; #ifdef S_IFBLK else if ((st->st_mode & S_IFMT) == S_IFBLK) info->fi_type = _FILE_TYPE_BLOCK; #endif #ifdef S_IFCHR else if ((st->st_mode & S_IFMT) == S_IFCHR) info->fi_type = _FILE_TYPE_CHAR; #endif #ifdef S_IFIFO else if ((st->st_mode & S_IFMT) == S_IFIFO) info->fi_type = _FILE_TYPE_FIFO; #endif #ifdef S_IFLNK else if ((st->st_mode & S_IFMT) == S_IFLNK) info->fi_type = _FILE_TYPE_SYMLINK; #endif #ifdef S_IFSOCK else if ((st->st_mode & S_IFMT) == S_IFSOCK) info->fi_type = _FILE_TYPE_SOCKET; #endif else info->fi_type = _FILE_TYPE_UNKNOWN; /* Convert the file permissions */ info->fi_perms = 0x0000; if ((st->st_mode & S_IREAD) != 0) info->fi_perms |= _FILE_PERM_READ; if ((st->st_mode & S_IWRITE) != 0) info->fi_perms |= _FILE_PERM_WRITE; if ((st->st_mode & S_IEXEC) != 0 and (st->st_mode & S_IFREG) != 0) info->fi_perms |= _FILE_PERM_EXEC; if ((st->st_mode & S_IEXEC) != 0 and (st->st_mode & S_IFDIR) != 0) info->fi_perms |= _FILE_PERM_SEARCH; /* Convert the file serial number */ #if defined(_WIN32) info->fi_id = -1; /* Indeterminate */ #else info->fi_id = st->st_ino; #endif /* Convert the file system ID */ v = st->st_dev; sprintf(info->fi_filesys, "%lX", &v); /* Convert the file size */ info->fi_size = st->st_size; /* Convert the file dates */ info->fi_modified = st->st_mtime; info->fi_accessed = maxt(st->st_atime, st->st_ctime); info->fi_created = _TIME_UNKNOWN; info->fi_changed = st->st_ctime; return (0); } /*------------------------------------------------------------------------------ * _getfileinfo() * Retrieve information about a file name. * * @param fname * File or directory name. * * @param info * Pointer to a file information structure. * * @return * Zero if successful and the structure pointed to by 'info' is filled with * information about the file; otherwise -1 and 'errno' is set. * * @see * * * @since 1.1, 2003-10-07 */ int _getfileinfo(const char *fname, struct _fileinfo *info) { struct stat st; /* Retrieve info about a filename */ if (stat(fname, &st) < 0) return (-1); /* Fill in the file info */ return (convert_info(&st, info)); } /*------------------------------------------------------------------------------ * _fgetfileinfo() * Retrieve information about an I/O stream. * * @param fp * An I/O stream. * This should not be null, nor should it point to a closed stream. * * @param info * Pointer to a file information structure. * * @return * Zero if successful and the structure pointed to by 'info' is filled with * information about the stream; otherwise -1. * * @see * * * @since 1.1, 2003-10-07 */ int _fgetfileinfo(FILE *fp, struct _fileinfo *info) { struct stat st; /* Retrieve info about a filename */ if (fstat(fileno(fp), &st) < 0) return (-1); /* Fill in the file info */ return (convert_info(&st, info)); } #if !NOTEST /*------------------------------------------------------------------------------ * print_info() * Print information about a filename to stdout. * * @param fname * Name of a file. * * @param info * Information about the file. * * @see * * * @since 1.1, 2003-10-07 */ static void print_info(const char *fname, const struct _fileinfo *info) { #if _FILEINFO_VERS != 2 #error Struct _fileinfo has changed #endif long int v; char ch; char tbuf[40]; /* Display the file type */ switch (info->fi_type) { case _FILE_TYPE_FILE: ch = 'f'; break; case _FILE_TYPE_DIR: ch = 'd'; break; #ifdef _FILE_TYPE_BLOCK case _FILE_TYPE_BLOCK: ch = 'b'; break; #endif #ifdef _FILE_TYPE_CHAR case _FILE_TYPE_CHAR: ch = 'c'; break; #endif #ifdef _FILE_TYPE_FIFO case _FILE_TYPE_FIFO: ch = 'p'; break; #endif #ifdef _FILE_TYPE_SYMLINK case _FILE_TYPE_SYMLINK: ch = 'l'; break; #endif #ifdef _FILE_TYPE_SOCKET case _FILE_TYPE_SOCKET: ch = 's'; break; #endif case _FILE_TYPE_UNKNOWN: default: ch = '?'; break; } printf("%c", ch); /* Display the file permissions */ printf((info->fi_perms & _FILE_PERM_READ) != 0 ? "r" : "-"); printf((info->fi_perms & _FILE_PERM_WRITE) != 0 ? "w" : "-"); printf((info->fi_perms & _FILE_PERM_EXEC) != 0 ? "x" : (info->fi_perms & _FILE_PERM_SEARCH) != 0 ? "s" : "-"); /* Display the file size */ printf(" %10" LLONG_FMT, info->fi_size); /* Display the file modification time */ if (info->fi_modified != _TIME_UNKNOWN) { struct tm ts; ts = *localtime(&info->fi_modified); strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M", &ts); } else sprintf(tbuf, "unknown"); printf(" %16s", tbuf); #if DEBUGS printf(" [a=%ld m=%ld s=%ld]", info->fi_accessed, info->fi_modified, info->fi_changed); #endif /* Display the file system ID */ strcpy(tbuf, info->fi_filesys); /* Display the file serial number */ if (info->fi_id != -1) sprintf(tbuf+strlen(tbuf), ":%ld", info->fi_id); else sprintf(tbuf+strlen(tbuf), ":-"); printf(" %5s", tbuf); /* Display the file name */ printf(" %s", fname); printf("\n"); } #endif /* NOTEST */ #if !NOTEST /*------------------------------------------------------------------------------ * main() * Test driver. * Display information about one or more files. * * @usage * c0xfstat file... * * @param argc * Number of command line arguments in 'argv'. * * @param argv * Command line arguments. * * @since 1.1, 2003-10-07 */ int main(int argc, char **argv) { int i; /* Check args */ if (argc < 2) { /* Display a usage message */ printf("Display information about one or more files.\n"); printf("\n"); printf("usage: %s file...\n", PROG); printf("\n"); /* Punt */ return (EXIT_FAILURE); } /* Process one or more filename args */ for (i = 1; i < argc; i++) { const char * fname; struct _fileinfo info; /* Retrieve info for a filename and print it */ fname = argv[i]; if (_getfileinfo(fname, &info) < 0) printf("Error (%d): %s: %s\n", errno, strerror(errno), fname); else print_info(fname, &info); } return (EXIT_SUCCESS); } #endif /* NOTEST */ /* End c0xfstat.c */