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