1/* listfile.c -- display a long listing of a file 2 Copyright (C) 1991, 1993, 2000, 2003, 2004, 2007 Free Software 3 Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17*/ 18 19#if HAVE_CONFIG_H 20# include <config.h> 21#endif 22 23#include <alloca.h> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <stdio.h> 27#include <pwd.h> 28#include <grp.h> 29#include <time.h> 30#include <errno.h> 31#include "human.h" 32#include "xalloc.h" 33#include "pathmax.h" 34#include "error.h" 35#include "filemode.h" 36#include "idcache.h" 37 38#include "listfile.h" 39 40#if HAVE_STRING_H || STDC_HEADERS 41#include <string.h> 42#else 43#include <strings.h> 44#endif 45 46 47/* The presence of unistd.h is assumed by gnulib these days, so we 48 * might as well assume it too. 49 */ 50#include <unistd.h> /* for readlink() */ 51 52 53#if STDC_HEADERS 54# include <stdlib.h> 55#else 56char *getenv (); 57extern int errno; 58#endif 59 60/* Since major is a function on SVR4, we can't use `ifndef major'. */ 61#ifdef MAJOR_IN_MKDEV 62#include <sys/mkdev.h> 63#define HAVE_MAJOR 64#endif 65#ifdef MAJOR_IN_SYSMACROS 66#include <sys/sysmacros.h> 67#define HAVE_MAJOR 68#endif 69 70#ifdef STAT_MACROS_BROKEN 71#undef S_ISCHR 72#undef S_ISBLK 73#undef S_ISLNK 74#endif 75 76#ifndef S_ISCHR 77#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 78#endif 79#ifndef S_ISBLK 80#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) 81#endif 82#if defined(S_IFLNK) && !defined(S_ISLNK) 83#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 84#endif 85 86/* Get or fake the disk device blocksize. 87 Usually defined by sys/param.h (if at all). */ 88#ifndef DEV_BSIZE 89# ifdef BSIZE 90# define DEV_BSIZE BSIZE 91# else /* !BSIZE */ 92# define DEV_BSIZE 4096 93# endif /* !BSIZE */ 94#endif /* !DEV_BSIZE */ 95 96/* Extract or fake data from a `struct stat'. 97 ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. 98 ST_NBLOCKS: Number of blocks in the file, including indirect blocks. 99 ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ 100#ifndef HAVE_STRUCT_STAT_ST_BLOCKS 101# define ST_BLKSIZE(statbuf) DEV_BSIZE 102# if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */ 103# define ST_NBLOCKS(statbuf) \ 104 (S_ISREG ((statbuf).st_mode) \ 105 || S_ISDIR ((statbuf).st_mode) \ 106 ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0) 107# else /* !_POSIX_SOURCE && BSIZE */ 108# define ST_NBLOCKS(statbuf) \ 109 (S_ISREG ((statbuf).st_mode) \ 110 || S_ISDIR ((statbuf).st_mode) \ 111 ? st_blocks ((statbuf).st_size) : 0) 112# endif /* !_POSIX_SOURCE && BSIZE */ 113#else /* HAVE_STRUCT_STAT_ST_BLOCKS */ 114/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ 115# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ 116 ? (statbuf).st_blksize : DEV_BSIZE) 117# if defined(hpux) || defined(__hpux__) || defined(__hpux) 118/* HP-UX counts st_blocks in 1024-byte units. 119 This loses when mixing HP-UX and BSD filesystems with NFS. */ 120# define ST_NBLOCKSIZE 1024 121# else /* !hpux */ 122# if defined(_AIX) && defined(_I386) 123/* AIX PS/2 counts st_blocks in 4K units. */ 124# define ST_NBLOCKSIZE (4 * 1024) 125# else /* not AIX PS/2 */ 126# if defined(_CRAY) 127# define ST_NBLOCKS(statbuf) \ 128 (S_ISREG ((statbuf).st_mode) \ 129 || S_ISDIR ((statbuf).st_mode) \ 130 ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) 131# endif /* _CRAY */ 132# endif /* not AIX PS/2 */ 133# endif /* !hpux */ 134#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ 135 136#ifndef ST_NBLOCKS 137# define ST_NBLOCKS(statbuf) \ 138 (S_ISREG ((statbuf).st_mode) \ 139 || S_ISDIR ((statbuf).st_mode) \ 140 ? (statbuf).st_blocks : 0) 141#endif 142 143#ifndef ST_NBLOCKSIZE 144# define ST_NBLOCKSIZE 512 145#endif 146 147#ifndef _POSIX_VERSION 148struct passwd *getpwuid (); 149struct group *getgrgid (); 150#endif 151 152#ifdef major /* Might be defined in sys/types.h. */ 153#define HAVE_MAJOR 154#endif 155#ifndef HAVE_MAJOR 156#define major(dev) (((dev) >> 8) & 0xff) 157#define minor(dev) ((dev) & 0xff) 158#endif 159#undef HAVE_MAJOR 160 161 162char * get_link_name (char *name, char *relname); 163static void print_name_with_quoting (register char *p, FILE *stream); 164 165 166/* NAME is the name to print. 167 RELNAME is the path to access it from the current directory. 168 STATP is the results of stat or lstat on it. 169 Use CURRENT_TIME to decide whether to print yyyy or hh:mm. 170 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts 171 and sizes. 172 STREAM is the stdio stream to print on. */ 173 174void 175list_file (char *name, 176 char *relname, 177 struct stat *statp, 178 time_t current_time, 179 int output_block_size, 180 FILE *stream) 181{ 182 char modebuf[12]; 183 struct tm const *when_local; 184 char const *user_name; 185 char const *group_name; 186 char hbuf[LONGEST_HUMAN_READABLE + 1]; 187 188#if HAVE_ST_DM_MODE 189 /* Cray DMF: look at the file's migrated, not real, status */ 190 strmode (statp->st_dm_mode, modebuf); 191#else 192 strmode (statp->st_mode, modebuf); 193#endif 194 195 fprintf (stream, "%6s ", 196 human_readable ((uintmax_t) statp->st_ino, hbuf, 197 human_ceiling, 198 1, 1)); 199 200 fprintf (stream, "%4s ", 201 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf, 202 human_ceiling, 203 ST_NBLOCKSIZE, output_block_size)); 204 205 206 /* modebuf includes the space between the mode and the number of links, 207 as the POSIX "optional alternate access method flag". */ 208 fprintf (stream, "%s%3lu ", modebuf, (unsigned long) statp->st_nlink); 209 210 user_name = getuser (statp->st_uid); 211 if (user_name) 212 fprintf (stream, "%-8s ", user_name); 213 else 214 fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid); 215 216 group_name = getgroup (statp->st_gid); 217 if (group_name) 218 fprintf (stream, "%-8s ", group_name); 219 else 220 fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid); 221 222 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode)) 223#ifdef HAVE_ST_RDEV 224 fprintf (stream, "%3lu, %3lu ", 225 (unsigned long) major (statp->st_rdev), 226 (unsigned long) minor (statp->st_rdev)); 227#else 228 fprintf (stream, " "); 229#endif 230 else 231 fprintf (stream, "%8s ", 232 human_readable ((uintmax_t) statp->st_size, hbuf, 233 human_ceiling, 234 1, 235 output_block_size < 0 ? output_block_size : 1)); 236 237 if ((when_local = localtime (&statp->st_mtime))) 238 { 239 char init_bigbuf[256]; 240 char *buf = init_bigbuf; 241 size_t bufsize = sizeof init_bigbuf; 242 243 /* Use strftime rather than ctime, because the former can produce 244 locale-dependent names for the month (%b). 245 246 Output the year if the file is fairly old or in the future. 247 POSIX says the cutoff is 6 months old; 248 approximate this by 6*30 days. 249 Allow a 1 hour slop factor for what is considered "the future", 250 to allow for NFS server/client clock disagreement. */ 251 char const *fmt = 252 ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime 253 && statp->st_mtime <= current_time + 60 * 60) 254 ? "%b %e %H:%M" 255 : "%b %e %Y"); 256 257 while (!strftime (buf, bufsize, fmt, when_local)) 258 buf = (char *) alloca (bufsize *= 2); 259 260 fprintf (stream, "%s ", buf); 261 } 262 else 263 { 264 /* The time cannot be represented as a local time; 265 print it as a huge integer number of seconds. */ 266 int width = 12; 267 268 if (statp->st_mtime < 0) 269 { 270 char const *num = human_readable (- (uintmax_t) statp->st_mtime, 271 hbuf, human_ceiling, 1, 1); 272 int sign_width = width - strlen (num); 273 fprintf (stream, "%*s%s ", 274 sign_width < 0 ? 0 : sign_width, "-", num); 275 } 276 else 277 fprintf (stream, "%*s ", width, 278 human_readable ((uintmax_t) statp->st_mtime, hbuf, 279 human_ceiling, 280 1, 1)); 281 } 282 283 print_name_with_quoting (name, stream); 284 285#ifdef S_ISLNK 286 if (S_ISLNK (statp->st_mode)) 287 { 288 char *linkname = get_link_name (name, relname); 289 290 if (linkname) 291 { 292 fputs (" -> ", stream); 293 print_name_with_quoting (linkname, stream); 294 free (linkname); 295 } 296 } 297#endif 298 putc ('\n', stream); 299} 300 301static void 302print_name_with_quoting (register char *p, FILE *stream) 303{ 304 register unsigned char c; 305 306 while ((c = *p++) != '\0') 307 { 308 switch (c) 309 { 310 case '\\': 311 fprintf (stream, "\\\\"); 312 break; 313 314 case '\n': 315 fprintf (stream, "\\n"); 316 break; 317 318 case '\b': 319 fprintf (stream, "\\b"); 320 break; 321 322 case '\r': 323 fprintf (stream, "\\r"); 324 break; 325 326 case '\t': 327 fprintf (stream, "\\t"); 328 break; 329 330 case '\f': 331 fprintf (stream, "\\f"); 332 break; 333 334 case ' ': 335 fprintf (stream, "\\ "); 336 break; 337 338 case '"': 339 fprintf (stream, "\\\""); 340 break; 341 342 default: 343 if (c > 040 && c < 0177) 344 putc (c, stream); 345 else 346 fprintf (stream, "\\%03o", (unsigned int) c); 347 } 348 } 349} 350 351#ifdef S_ISLNK 352char * 353get_link_name (char *name, char *relname) 354{ 355 register char *linkname; 356 register int linklen; 357 358 /* st_size is wrong for symlinks on AIX, and on 359 mount points with some automounters. 360 So allocate a pessimistic PATH_MAX + 1 bytes. */ 361#define LINK_BUF PATH_MAX 362 linkname = (char *) xmalloc (LINK_BUF + 1); 363 linklen = readlink (relname, linkname, LINK_BUF); 364 if (linklen < 0) 365 { 366 error (0, errno, "%s", name); 367 free (linkname); 368 return 0; 369 } 370 linkname[linklen] = '\0'; 371 return linkname; 372} 373#endif 374