Added Bas van Gompel.
[findutils.git] / lib / listfile.c
blob38d4642d65f1990b96a060b1b48143f6eca4a128
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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 "../gnulib/lib/human.h"
51 #include "../gnulib/lib/pathmax.h"
53 #if HAVE_STRING_H || STDC_HEADERS
54 #include <string.h>
55 #else
56 #include <strings.h>
57 #endif
59 #if STDC_HEADERS
60 # include <stdlib.h>
61 #else
62 char *getenv ();
63 extern int errno;
64 #endif
66 /* Since major is a function on SVR4, we can't use `ifndef major'. */
67 #ifdef MAJOR_IN_MKDEV
68 #include <sys/mkdev.h>
69 #define HAVE_MAJOR
70 #endif
71 #ifdef MAJOR_IN_SYSMACROS
72 #include <sys/sysmacros.h>
73 #define HAVE_MAJOR
74 #endif
76 #ifdef STAT_MACROS_BROKEN
77 #undef S_ISCHR
78 #undef S_ISBLK
79 #undef S_ISLNK
80 #endif
82 #ifndef S_ISCHR
83 #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
84 #endif
85 #ifndef S_ISBLK
86 #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
87 #endif
88 #if defined(S_IFLNK) && !defined(S_ISLNK)
89 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
90 #endif
92 #if defined(S_ISLNK)
93 int readlink ();
94 #endif
96 /* Get or fake the disk device blocksize.
97 Usually defined by sys/param.h (if at all). */
98 #ifndef DEV_BSIZE
99 # ifdef BSIZE
100 # define DEV_BSIZE BSIZE
101 # else /* !BSIZE */
102 # define DEV_BSIZE 4096
103 # endif /* !BSIZE */
104 #endif /* !DEV_BSIZE */
106 /* Extract or fake data from a `struct stat'.
107 ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
108 ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
109 ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
110 #ifndef HAVE_STRUCT_STAT_ST_BLOCKS
111 # define ST_BLKSIZE(statbuf) DEV_BSIZE
112 # if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */
113 # define ST_NBLOCKS(statbuf) \
114 (S_ISREG ((statbuf).st_mode) \
115 || S_ISDIR ((statbuf).st_mode) \
116 ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
117 # else /* !_POSIX_SOURCE && BSIZE */
118 # define ST_NBLOCKS(statbuf) \
119 (S_ISREG ((statbuf).st_mode) \
120 || S_ISDIR ((statbuf).st_mode) \
121 ? st_blocks ((statbuf).st_size) : 0)
122 # endif /* !_POSIX_SOURCE && BSIZE */
123 #else /* HAVE_STRUCT_STAT_ST_BLOCKS */
124 /* Some systems, like Sequents, return st_blksize of 0 on pipes. */
125 # define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
126 ? (statbuf).st_blksize : DEV_BSIZE)
127 # if defined(hpux) || defined(__hpux__) || defined(__hpux)
128 /* HP-UX counts st_blocks in 1024-byte units.
129 This loses when mixing HP-UX and BSD filesystems with NFS. */
130 # define ST_NBLOCKSIZE 1024
131 # else /* !hpux */
132 # if defined(_AIX) && defined(_I386)
133 /* AIX PS/2 counts st_blocks in 4K units. */
134 # define ST_NBLOCKSIZE (4 * 1024)
135 # else /* not AIX PS/2 */
136 # if defined(_CRAY)
137 # define ST_NBLOCKS(statbuf) \
138 (S_ISREG ((statbuf).st_mode) \
139 || S_ISDIR ((statbuf).st_mode) \
140 ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
141 # endif /* _CRAY */
142 # endif /* not AIX PS/2 */
143 # endif /* !hpux */
144 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
146 #ifndef ST_NBLOCKS
147 # define ST_NBLOCKS(statbuf) \
148 (S_ISREG ((statbuf).st_mode) \
149 || S_ISDIR ((statbuf).st_mode) \
150 ? (statbuf).st_blocks : 0)
151 #endif
153 #ifndef ST_NBLOCKSIZE
154 # define ST_NBLOCKSIZE 512
155 #endif
157 #ifndef _POSIX_VERSION
158 struct passwd *getpwuid ();
159 struct group *getgrgid ();
160 #endif
162 #ifdef major /* Might be defined in sys/types.h. */
163 #define HAVE_MAJOR
164 #endif
165 #ifndef HAVE_MAJOR
166 #define major(dev) (((dev) >> 8) & 0xff)
167 #define minor(dev) ((dev) & 0xff)
168 #endif
169 #undef HAVE_MAJOR
171 char *xmalloc ();
172 void error ();
173 void mode_string ();
175 char *get_link_name ();
176 char *getgroup ();
177 char *getuser ();
178 void print_name_with_quoting ();
180 /* NAME is the name to print.
181 RELNAME is the path to access it from the current directory.
182 STATP is the results of stat or lstat on it.
183 Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
184 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
185 and sizes.
186 STREAM is the stdio stream to print on. */
188 void
189 list_file (name, relname, statp, current_time, output_block_size, stream)
190 char *name;
191 char *relname;
192 struct stat *statp;
193 time_t current_time;
194 int output_block_size;
195 FILE *stream;
197 char modebuf[11];
198 struct tm const *when_local;
199 char const *user_name;
200 char const *group_name;
201 char hbuf[LONGEST_HUMAN_READABLE + 1];
203 #if HAVE_ST_DM_MODE
204 /* Cray DMF: look at the file's migrated, not real, status */
205 mode_string (statp->st_dm_mode, modebuf);
206 #else
207 mode_string (statp->st_mode, modebuf);
208 #endif
209 modebuf[10] = '\0';
211 fprintf (stream, "%6s ",
212 human_readable ((uintmax_t) statp->st_ino, hbuf,
213 human_ceiling,
214 1, 1));
216 fprintf (stream, "%4s ",
217 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
218 human_ceiling,
219 ST_NBLOCKSIZE, output_block_size));
222 /* The space between the mode and the number of links is the POSIX
223 "optional alternate access method flag". */
224 fprintf (stream, "%s %3lu ", modebuf, (unsigned long) statp->st_nlink);
226 user_name = getuser (statp->st_uid);
227 if (user_name)
228 fprintf (stream, "%-8s ", user_name);
229 else
230 fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
232 group_name = getgroup (statp->st_gid);
233 if (group_name)
234 fprintf (stream, "%-8s ", group_name);
235 else
236 fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid);
238 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
239 #ifdef HAVE_ST_RDEV
240 fprintf (stream, "%3lu, %3lu ",
241 (unsigned long) major (statp->st_rdev),
242 (unsigned long) minor (statp->st_rdev));
243 #else
244 fprintf (stream, " ");
245 #endif
246 else
247 fprintf (stream, "%8s ",
248 human_readable ((uintmax_t) statp->st_size, hbuf,
249 human_ceiling,
251 output_block_size < 0 ? output_block_size : 1));
253 if ((when_local = localtime (&statp->st_mtime)))
255 char init_bigbuf[256];
256 char *buf = init_bigbuf;
257 size_t bufsize = sizeof init_bigbuf;
259 /* Use strftime rather than ctime, because the former can produce
260 locale-dependent names for the month (%b).
262 Output the year if the file is fairly old or in the future.
263 POSIX says the cutoff is 6 months old;
264 approximate this by 6*30 days.
265 Allow a 1 hour slop factor for what is considered "the future",
266 to allow for NFS server/client clock disagreement. */
267 char const *fmt =
268 ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
269 && statp->st_mtime <= current_time + 60 * 60)
270 ? "%b %e %H:%M"
271 : "%b %e %Y");
273 while (!strftime (buf, bufsize, fmt, when_local))
274 buf = (char *) alloca (bufsize *= 2);
276 fprintf (stream, "%s ", buf);
278 else
280 /* The time cannot be represented as a local time;
281 print it as a huge integer number of seconds. */
282 int width = 12;
284 if (statp->st_mtime < 0)
286 char const *num = human_readable (- (uintmax_t) statp->st_mtime,
287 hbuf, human_ceiling, 1, 1);
288 int sign_width = width - strlen (num);
289 fprintf (stream, "%*s%s ",
290 sign_width < 0 ? 0 : sign_width, "-", num);
292 else
293 fprintf (stream, "%*s ", width,
294 human_readable ((uintmax_t) statp->st_mtime, hbuf,
295 human_ceiling,
296 1, 1));
299 print_name_with_quoting (name, stream);
301 #ifdef S_ISLNK
302 if (S_ISLNK (statp->st_mode))
304 char *linkname = get_link_name (name, relname);
306 if (linkname)
308 fputs (" -> ", stream);
309 print_name_with_quoting (linkname, stream);
310 free (linkname);
313 #endif
314 putc ('\n', stream);
317 void
318 print_name_with_quoting (p, stream)
319 register char *p;
320 FILE *stream;
322 register unsigned char c;
324 while ((c = *p++) != '\0')
326 switch (c)
328 case '\\':
329 fprintf (stream, "\\\\");
330 break;
332 case '\n':
333 fprintf (stream, "\\n");
334 break;
336 case '\b':
337 fprintf (stream, "\\b");
338 break;
340 case '\r':
341 fprintf (stream, "\\r");
342 break;
344 case '\t':
345 fprintf (stream, "\\t");
346 break;
348 case '\f':
349 fprintf (stream, "\\f");
350 break;
352 case ' ':
353 fprintf (stream, "\\ ");
354 break;
356 case '"':
357 fprintf (stream, "\\\"");
358 break;
360 default:
361 if (c > 040 && c < 0177)
362 putc (c, stream);
363 else
364 fprintf (stream, "\\%03o", (unsigned int) c);
369 #ifdef S_ISLNK
370 char *
371 get_link_name (name, relname)
372 char *name;
373 char *relname;
375 register char *linkname;
376 register int linklen;
378 /* st_size is wrong for symlinks on AIX, and on
379 mount points with some automounters.
380 So allocate a pessimistic PATH_MAX + 1 bytes. */
381 #define LINK_BUF PATH_MAX
382 linkname = (char *) xmalloc (LINK_BUF + 1);
383 linklen = readlink (relname, linkname, LINK_BUF);
384 if (linklen < 0)
386 error (0, errno, "%s", name);
387 free (linkname);
388 return 0;
390 linkname[linklen] = '\0';
391 return linkname;
393 #endif