Support fts() in FTS_CWDFD mode. Also work around Savannah bug #18466 (with a perfor...
[findutils.git] / lib / listfile.c
blob92f85577ffde3eb033eb0e45a390ff6ed2a83758
1 /* listfile.c -- display a long listing of a file
2 Copyright (C) 1991, 1993, 2000 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17 USA.
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
24 #ifndef __GNUC__
25 # if HAVE_ALLOCA_H
26 # include <alloca.h>
27 # else
28 # ifdef _AIX
29 # pragma alloca
30 # else
31 # ifdef _WIN32
32 # include <malloc.h>
33 # include <io.h>
34 # else
35 # ifndef alloca
36 char *alloca ();
37 # endif
38 # endif
39 # endif
40 # endif
41 #endif
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <stdio.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include <time.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <openat.h>
52 #include "human.h"
53 #include "xalloc.h"
54 #include "pathmax.h"
55 #include "error.h"
56 #include "filemode.h"
58 #include "listfile.h"
60 #if HAVE_STRING_H || STDC_HEADERS
61 #include <string.h>
62 #else
63 #include <strings.h>
64 #endif
67 /* The presence of unistd.h is assumed by gnulib these days, so we
68 * might as well assume it too.
70 #include <unistd.h> /* for readlink() */
73 #if STDC_HEADERS
74 # include <stdlib.h>
75 #else
76 char *getenv ();
77 extern int errno;
78 #endif
80 /* Since major is a function on SVR4, we can't use `ifndef major'. */
81 #ifdef MAJOR_IN_MKDEV
82 #include <sys/mkdev.h>
83 #define HAVE_MAJOR
84 #endif
85 #ifdef MAJOR_IN_SYSMACROS
86 #include <sys/sysmacros.h>
87 #define HAVE_MAJOR
88 #endif
94 #ifdef HAVE_LOCALE_H
95 #include <locale.h>
96 #endif
98 #if ENABLE_NLS
99 # include <libintl.h>
100 # define _(Text) gettext (Text)
101 #else
102 # define _(Text) Text
103 #define textdomain(Domain)
104 #define bindtextdomain(Package, Directory)
105 #endif
106 #ifdef gettext_noop
107 # define N_(String) gettext_noop (String)
108 #else
109 /* See locate.c for explanation as to why not use (String) */
110 # define N_(String) String
111 #endif
115 #ifdef STAT_MACROS_BROKEN
116 #undef S_ISCHR
117 #undef S_ISBLK
118 #undef S_ISLNK
119 #endif
121 #ifndef S_ISCHR
122 #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
123 #endif
124 #ifndef S_ISBLK
125 #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
126 #endif
127 #if defined(S_IFLNK) && !defined(S_ISLNK)
128 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
129 #endif
131 /* Get or fake the disk device blocksize.
132 Usually defined by sys/param.h (if at all). */
133 #ifndef DEV_BSIZE
134 # ifdef BSIZE
135 # define DEV_BSIZE BSIZE
136 # else /* !BSIZE */
137 # define DEV_BSIZE 4096
138 # endif /* !BSIZE */
139 #endif /* !DEV_BSIZE */
141 /* Extract or fake data from a `struct stat'.
142 ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
143 ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
144 ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
145 #ifndef HAVE_STRUCT_STAT_ST_BLOCKS
146 # define ST_BLKSIZE(statbuf) DEV_BSIZE
147 # if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */
148 # define ST_NBLOCKS(statbuf) \
149 (S_ISREG ((statbuf).st_mode) \
150 || S_ISDIR ((statbuf).st_mode) \
151 ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
152 # else /* !_POSIX_SOURCE && BSIZE */
153 # define ST_NBLOCKS(statbuf) \
154 (S_ISREG ((statbuf).st_mode) \
155 || S_ISDIR ((statbuf).st_mode) \
156 ? st_blocks ((statbuf).st_size) : 0)
157 # endif /* !_POSIX_SOURCE && BSIZE */
158 #else /* HAVE_STRUCT_STAT_ST_BLOCKS */
159 /* Some systems, like Sequents, return st_blksize of 0 on pipes. */
160 # define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
161 ? (statbuf).st_blksize : DEV_BSIZE)
162 # if defined(hpux) || defined(__hpux__) || defined(__hpux)
163 /* HP-UX counts st_blocks in 1024-byte units.
164 This loses when mixing HP-UX and BSD filesystems with NFS. */
165 # define ST_NBLOCKSIZE 1024
166 # else /* !hpux */
167 # if defined(_AIX) && defined(_I386)
168 /* AIX PS/2 counts st_blocks in 4K units. */
169 # define ST_NBLOCKSIZE (4 * 1024)
170 # else /* not AIX PS/2 */
171 # if defined(_CRAY)
172 # define ST_NBLOCKS(statbuf) \
173 (S_ISREG ((statbuf).st_mode) \
174 || S_ISDIR ((statbuf).st_mode) \
175 ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
176 # endif /* _CRAY */
177 # endif /* not AIX PS/2 */
178 # endif /* !hpux */
179 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
181 #ifndef ST_NBLOCKS
182 # define ST_NBLOCKS(statbuf) \
183 (S_ISREG ((statbuf).st_mode) \
184 || S_ISDIR ((statbuf).st_mode) \
185 ? (statbuf).st_blocks : 0)
186 #endif
188 #ifndef ST_NBLOCKSIZE
189 # define ST_NBLOCKSIZE 512
190 #endif
192 #ifndef _POSIX_VERSION
193 struct passwd *getpwuid ();
194 struct group *getgrgid ();
195 #endif
197 #ifdef major /* Might be defined in sys/types.h. */
198 #define HAVE_MAJOR
199 #endif
200 #ifndef HAVE_MAJOR
201 #define major(dev) (((dev) >> 8) & 0xff)
202 #define minor(dev) ((dev) & 0xff)
203 #endif
204 #undef HAVE_MAJOR
207 static void print_name (register char *p, FILE *stream, int literal_control_chars);
209 extern char * getgroup (gid_t gid);
210 extern char * getuser (uid_t uid);
214 size_t
215 file_blocksize(const struct stat *p)
217 return ST_NBLOCKSIZE;
222 /* NAME is the name to print.
223 RELNAME is the path to access it from the current directory.
224 STATP is the results of stat or lstat on it.
225 Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
226 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
227 and sizes.
228 STREAM is the stdio stream to print on. */
230 void
231 list_file (char *name,
232 int dirfd,
233 char *relname,
234 const struct stat *statp,
235 time_t current_time,
236 int output_block_size,
237 int literal_control_chars,
238 FILE *stream)
240 char modebuf[11];
241 struct tm const *when_local;
242 char const *user_name;
243 char const *group_name;
244 char hbuf[LONGEST_HUMAN_READABLE + 1];
246 #if HAVE_ST_DM_MODE
247 /* Cray DMF: look at the file's migrated, not real, status */
248 strmode (statp->st_dm_mode, modebuf);
249 #else
250 strmode (statp->st_mode, modebuf);
251 #endif
252 modebuf[10] = '\0';
254 fprintf (stream, "%6s ",
255 human_readable ((uintmax_t) statp->st_ino, hbuf,
256 human_ceiling,
257 1, 1));
259 fprintf (stream, "%4s ",
260 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
261 human_ceiling,
262 ST_NBLOCKSIZE, output_block_size));
265 /* The space between the mode and the number of links is the POSIX
266 "optional alternate access method flag". */
267 fprintf (stream, "%s %3lu ", modebuf, (unsigned long) statp->st_nlink);
269 user_name = getuser (statp->st_uid);
270 if (user_name)
271 fprintf (stream, "%-8s ", user_name);
272 else
273 fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
275 group_name = getgroup (statp->st_gid);
276 if (group_name)
277 fprintf (stream, "%-8s ", group_name);
278 else
279 fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid);
281 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
282 #ifdef HAVE_ST_RDEV
283 fprintf (stream, "%3lu, %3lu ",
284 (unsigned long) major (statp->st_rdev),
285 (unsigned long) minor (statp->st_rdev));
286 #else
287 fprintf (stream, " ");
288 #endif
289 else
290 fprintf (stream, "%8s ",
291 human_readable ((uintmax_t) statp->st_size, hbuf,
292 human_ceiling,
294 output_block_size < 0 ? output_block_size : 1));
296 if ((when_local = localtime (&statp->st_mtime)))
298 char init_bigbuf[256];
299 char *buf = init_bigbuf;
300 size_t bufsize = sizeof init_bigbuf;
302 /* Use strftime rather than ctime, because the former can produce
303 locale-dependent names for the month (%b).
305 Output the year if the file is fairly old or in the future.
306 POSIX says the cutoff is 6 months old;
307 approximate this by 6*30 days.
308 Allow a 1 hour slop factor for what is considered "the future",
309 to allow for NFS server/client clock disagreement. */
310 char const *fmt =
311 ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
312 && statp->st_mtime <= current_time + 60 * 60)
313 ? "%b %e %H:%M"
314 : "%b %e %Y");
316 while (!strftime (buf, bufsize, fmt, when_local))
317 buf = (char *) alloca (bufsize *= 2);
319 fprintf (stream, "%s ", buf);
321 else
323 /* The time cannot be represented as a local time;
324 print it as a huge integer number of seconds. */
325 int width = 12;
327 if (statp->st_mtime < 0)
329 char const *num = human_readable (- (uintmax_t) statp->st_mtime,
330 hbuf, human_ceiling, 1, 1);
331 int sign_width = width - strlen (num);
332 fprintf (stream, "%*s%s ",
333 sign_width < 0 ? 0 : sign_width, "-", num);
335 else
336 fprintf (stream, "%*s ", width,
337 human_readable ((uintmax_t) statp->st_mtime, hbuf,
338 human_ceiling,
339 1, 1));
342 print_name (name, stream, literal_control_chars);
344 #ifdef S_ISLNK
345 if (S_ISLNK (statp->st_mode))
347 char *linkname = get_link_name_at (name, dirfd, relname);
349 if (linkname)
351 fputs (" -> ", stream);
352 print_name (linkname, stream, literal_control_chars);
353 free (linkname);
356 #endif
357 putc ('\n', stream);
361 static void
362 print_name_without_quoting (char *p, FILE *stream)
364 fprintf(stream, "%s", p);
368 static void
369 print_name_with_quoting (register char *p, FILE *stream)
371 register unsigned char c;
373 while ((c = *p++) != '\0')
375 switch (c)
377 case '\\':
378 fprintf (stream, "\\\\");
379 break;
381 case '\n':
382 fprintf (stream, "\\n");
383 break;
385 case '\b':
386 fprintf (stream, "\\b");
387 break;
389 case '\r':
390 fprintf (stream, "\\r");
391 break;
393 case '\t':
394 fprintf (stream, "\\t");
395 break;
397 case '\f':
398 fprintf (stream, "\\f");
399 break;
401 case ' ':
402 fprintf (stream, "\\ ");
403 break;
405 case '"':
406 fprintf (stream, "\\\"");
407 break;
409 default:
410 if (c > 040 && c < 0177)
411 putc (c, stream);
412 else
413 fprintf (stream, "\\%03o", (unsigned int) c);
418 static void print_name (register char *p, FILE *stream, int literal_control_chars)
420 if (literal_control_chars)
421 print_name_without_quoting(p, stream);
422 else
423 print_name_with_quoting(p, stream);
426 #ifdef S_ISLNK
427 static char *
428 get_link_name (const char *name, char *relname)
430 register char *linkname;
431 register int linklen;
433 /* st_size is wrong for symlinks on AIX, and on
434 mount points with some automounters.
435 So allocate a pessimistic PATH_MAX + 1 bytes. */
436 #define LINK_BUF PATH_MAX
437 linkname = (char *) xmalloc (LINK_BUF + 1);
438 linklen = readlink (relname, linkname, LINK_BUF);
439 if (linklen < 0)
441 error (0, errno, "%s", name);
442 free (linkname);
443 return 0;
445 linkname[linklen] = '\0';
446 return linkname;
449 struct link_name_args
451 const char *name;
452 char *relname;
453 char *result;
456 static int
457 get_link_name_cb(void *context)
459 struct link_name_args *args = context;
460 args->result = get_link_name(args->name, args->relname);
461 return 0;
464 char *
465 get_link_name_at (const char *name, int dirfd, char *relname)
467 struct link_name_args args;
468 args.result = NULL;
469 args.name = name;
470 args.relname = relname;
471 if (0 == run_in_dir(dirfd, get_link_name_cb, &args))
472 return args.result;
473 else
474 return NULL;
478 #endif