Applied Savannah patch #1547 (document the fact that printf
[findutils.git] / lib / listfile.c
blob322a1cb51e9803f2390a24f37307aa4292493071
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., 9 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA.
20 #if HAVE_CONFIG_H
21 # include <gnulib/config.h>
22 # undef VERSION
23 # undef PACKAGE_VERSION
24 # undef PACKAGE_TARNAME
25 # undef PACKAGE_STRING
26 # undef PACKAGE_NAME
27 # undef PACKAGE
28 # include <config.h>
29 #endif
31 #ifndef __GNUC__
32 # if HAVE_ALLOCA_H
33 # include <alloca.h>
34 # else
35 # ifdef _AIX
36 # pragma alloca
37 # else
38 # ifdef _WIN32
39 # include <malloc.h>
40 # include <io.h>
41 # else
42 # ifndef alloca
43 char *alloca ();
44 # endif
45 # endif
46 # endif
47 # endif
48 #endif
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <stdio.h>
53 #include <pwd.h>
54 #include <grp.h>
55 #include <time.h>
56 #include <errno.h>
57 #include "human.h"
58 #include "pathmax.h"
60 #if HAVE_STRING_H || STDC_HEADERS
61 #include <string.h>
62 #else
63 #include <strings.h>
64 #endif
66 #if STDC_HEADERS
67 # include <stdlib.h>
68 #else
69 char *getenv ();
70 extern int errno;
71 #endif
73 /* Since major is a function on SVR4, we can't use `ifndef major'. */
74 #ifdef MAJOR_IN_MKDEV
75 #include <sys/mkdev.h>
76 #define HAVE_MAJOR
77 #endif
78 #ifdef MAJOR_IN_SYSMACROS
79 #include <sys/sysmacros.h>
80 #define HAVE_MAJOR
81 #endif
83 #ifdef STAT_MACROS_BROKEN
84 #undef S_ISCHR
85 #undef S_ISBLK
86 #undef S_ISLNK
87 #endif
89 #ifndef S_ISCHR
90 #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
91 #endif
92 #ifndef S_ISBLK
93 #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
94 #endif
95 #if defined(S_IFLNK) && !defined(S_ISLNK)
96 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
97 #endif
99 #if defined(S_ISLNK)
100 int readlink ();
101 #endif
103 /* Get or fake the disk device blocksize.
104 Usually defined by sys/param.h (if at all). */
105 #ifndef DEV_BSIZE
106 # ifdef BSIZE
107 # define DEV_BSIZE BSIZE
108 # else /* !BSIZE */
109 # define DEV_BSIZE 4096
110 # endif /* !BSIZE */
111 #endif /* !DEV_BSIZE */
113 /* Extract or fake data from a `struct stat'.
114 ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
115 ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
116 ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
117 #ifndef HAVE_STRUCT_STAT_ST_BLOCKS
118 # define ST_BLKSIZE(statbuf) DEV_BSIZE
119 # if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */
120 # define ST_NBLOCKS(statbuf) \
121 (S_ISREG ((statbuf).st_mode) \
122 || S_ISDIR ((statbuf).st_mode) \
123 ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
124 # else /* !_POSIX_SOURCE && BSIZE */
125 # define ST_NBLOCKS(statbuf) \
126 (S_ISREG ((statbuf).st_mode) \
127 || S_ISDIR ((statbuf).st_mode) \
128 ? st_blocks ((statbuf).st_size) : 0)
129 # endif /* !_POSIX_SOURCE && BSIZE */
130 #else /* HAVE_STRUCT_STAT_ST_BLOCKS */
131 /* Some systems, like Sequents, return st_blksize of 0 on pipes. */
132 # define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
133 ? (statbuf).st_blksize : DEV_BSIZE)
134 # if defined(hpux) || defined(__hpux__) || defined(__hpux)
135 /* HP-UX counts st_blocks in 1024-byte units.
136 This loses when mixing HP-UX and BSD filesystems with NFS. */
137 # define ST_NBLOCKSIZE 1024
138 # else /* !hpux */
139 # if defined(_AIX) && defined(_I386)
140 /* AIX PS/2 counts st_blocks in 4K units. */
141 # define ST_NBLOCKSIZE (4 * 1024)
142 # else /* not AIX PS/2 */
143 # if defined(_CRAY)
144 # define ST_NBLOCKS(statbuf) \
145 (S_ISREG ((statbuf).st_mode) \
146 || S_ISDIR ((statbuf).st_mode) \
147 ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
148 # endif /* _CRAY */
149 # endif /* not AIX PS/2 */
150 # endif /* !hpux */
151 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
153 #ifndef ST_NBLOCKS
154 # define ST_NBLOCKS(statbuf) \
155 (S_ISREG ((statbuf).st_mode) \
156 || S_ISDIR ((statbuf).st_mode) \
157 ? (statbuf).st_blocks : 0)
158 #endif
160 #ifndef ST_NBLOCKSIZE
161 # define ST_NBLOCKSIZE 512
162 #endif
164 #ifndef _POSIX_VERSION
165 struct passwd *getpwuid ();
166 struct group *getgrgid ();
167 #endif
169 #ifdef major /* Might be defined in sys/types.h. */
170 #define HAVE_MAJOR
171 #endif
172 #ifndef HAVE_MAJOR
173 #define major(dev) (((dev) >> 8) & 0xff)
174 #define minor(dev) ((dev) & 0xff)
175 #endif
176 #undef HAVE_MAJOR
178 char *xmalloc ();
179 void error ();
180 void mode_string ();
182 char *get_link_name ();
183 char *getgroup ();
184 char *getuser ();
185 void print_name_with_quoting ();
187 /* NAME is the name to print.
188 RELNAME is the path to access it from the current directory.
189 STATP is the results of stat or lstat on it.
190 Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
191 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
192 and sizes.
193 STREAM is the stdio stream to print on. */
195 void
196 list_file (name, relname, statp, current_time, output_block_size, stream)
197 char *name;
198 char *relname;
199 struct stat *statp;
200 time_t current_time;
201 int output_block_size;
202 FILE *stream;
204 char modebuf[11];
205 struct tm const *when_local;
206 char const *user_name;
207 char const *group_name;
208 char hbuf[LONGEST_HUMAN_READABLE + 1];
210 #if HAVE_ST_DM_MODE
211 /* Cray DMF: look at the file's migrated, not real, status */
212 mode_string (statp->st_dm_mode, modebuf);
213 #else
214 mode_string (statp->st_mode, modebuf);
215 #endif
216 modebuf[10] = '\0';
218 fprintf (stream, "%6s ",
219 human_readable ((uintmax_t) statp->st_ino, hbuf, human_ceiling,
220 1, 1));
222 fprintf (stream, "%4s ",
223 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
224 human_ceiling,
225 ST_NBLOCKSIZE, output_block_size));
227 /* The space between the mode and the number of links is the POSIX
228 "optional alternate access method flag". */
229 fprintf (stream, "%s %3lu ", modebuf, (unsigned long) statp->st_nlink);
231 user_name = getuser (statp->st_uid);
232 if (user_name)
233 fprintf (stream, "%-8.8s ", user_name);
234 else
235 fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
237 group_name = getgroup (statp->st_gid);
238 if (group_name)
239 fprintf (stream, "%-8.8s ", group_name);
240 else
241 fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid);
243 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
244 #ifdef HAVE_ST_RDEV
245 fprintf (stream, "%3lu, %3lu ",
246 (unsigned long) major (statp->st_rdev),
247 (unsigned long) minor (statp->st_rdev));
248 #else
249 fprintf (stream, " ");
250 #endif
251 else
252 fprintf (stream, "%8s ",
253 human_readable ((uintmax_t) statp->st_size, hbuf, human_ceiling,
255 output_block_size < 0 ? output_block_size : 1));
257 if ((when_local = localtime (&statp->st_mtime)))
259 char init_bigbuf[256];
260 char *buf = init_bigbuf;
261 size_t bufsize = sizeof init_bigbuf;
263 /* Use strftime rather than ctime, because the former can produce
264 locale-dependent names for the month (%b).
266 Output the year if the file is fairly old or in the future.
267 POSIX says the cutoff is 6 months old;
268 approximate this by 6*30 days.
269 Allow a 1 hour slop factor for what is considered "the future",
270 to allow for NFS server/client clock disagreement. */
271 char const *fmt =
272 ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
273 && statp->st_mtime <= current_time + 60 * 60)
274 ? "%b %e %H:%M"
275 : "%b %e %Y");
277 while (!strftime (buf, bufsize, fmt, when_local))
278 buf = (char *) alloca (bufsize *= 2);
280 fprintf (stream, "%s ", buf);
282 else
284 /* The time cannot be represented as a local time;
285 print it as a huge integer number of seconds. */
286 int width = 12;
288 if (statp->st_mtime < 0)
290 char const *num = human_readable (- (uintmax_t) statp->st_mtime,
291 hbuf, human_ceiling, 1, 1);
292 int sign_width = width - strlen (num);
293 fprintf (stream, "%*s%s ",
294 sign_width < 0 ? 0 : sign_width, "-", num);
296 else
297 fprintf (stream, "%*s ", width,
298 human_readable ((uintmax_t) statp->st_mtime, hbuf,
299 human_ceiling,
300 1, 1));
303 print_name_with_quoting (name, stream);
305 #ifdef S_ISLNK
306 if (S_ISLNK (statp->st_mode))
308 char *linkname = get_link_name (name, relname);
310 if (linkname)
312 fputs (" -> ", stream);
313 print_name_with_quoting (linkname, stream);
314 free (linkname);
317 #endif
318 putc ('\n', stream);
321 void
322 print_name_with_quoting (p, stream)
323 register char *p;
324 FILE *stream;
326 register unsigned char c;
328 while ((c = *p++) != '\0')
330 switch (c)
332 case '\\':
333 fprintf (stream, "\\\\");
334 break;
336 case '\n':
337 fprintf (stream, "\\n");
338 break;
340 case '\b':
341 fprintf (stream, "\\b");
342 break;
344 case '\r':
345 fprintf (stream, "\\r");
346 break;
348 case '\t':
349 fprintf (stream, "\\t");
350 break;
352 case '\f':
353 fprintf (stream, "\\f");
354 break;
356 case ' ':
357 fprintf (stream, "\\ ");
358 break;
360 case '"':
361 fprintf (stream, "\\\"");
362 break;
364 default:
365 if (c > 040 && c < 0177)
366 putc (c, stream);
367 else
368 fprintf (stream, "\\%03o", (unsigned int) c);
373 #ifdef S_ISLNK
374 char *
375 get_link_name (name, relname)
376 char *name;
377 char *relname;
379 register char *linkname;
380 register int linklen;
382 /* st_size is wrong for symlinks on AIX, and on
383 mount points with some automounters.
384 So allocate a pessimistic PATH_MAX + 1 bytes. */
385 #define LINK_BUF PATH_MAX
386 linkname = (char *) xmalloc (LINK_BUF + 1);
387 linklen = readlink (relname, linkname, LINK_BUF);
388 if (linklen < 0)
390 error (0, errno, "%s", name);
391 free (linkname);
392 return 0;
394 linkname[linklen] = '\0';
395 return linkname;
397 #endif