MMove the normalisation into a procedure.
[findutils.git] / lib / listfile.c
blob9720bb34b41bcebf1205173cfff8fe0c0b6ed2b8
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 "human.h"
51 #include "xalloc.h"
52 #include "pathmax.h"
53 #include "error.h"
54 #include "filemode.h"
56 #include "listfile.h"
58 #if HAVE_STRING_H || STDC_HEADERS
59 #include <string.h>
60 #else
61 #include <strings.h>
62 #endif
65 #ifdef HAVE_UNISTD_H
66 #include <unistd.h> /* for readlink() */
67 #endif
69 #if STDC_HEADERS
70 # include <stdlib.h>
71 #else
72 char *getenv ();
73 extern int errno;
74 #endif
76 /* Since major is a function on SVR4, we can't use `ifndef major'. */
77 #ifdef MAJOR_IN_MKDEV
78 #include <sys/mkdev.h>
79 #define HAVE_MAJOR
80 #endif
81 #ifdef MAJOR_IN_SYSMACROS
82 #include <sys/sysmacros.h>
83 #define HAVE_MAJOR
84 #endif
86 #ifdef STAT_MACROS_BROKEN
87 #undef S_ISCHR
88 #undef S_ISBLK
89 #undef S_ISLNK
90 #endif
92 #ifndef S_ISCHR
93 #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
94 #endif
95 #ifndef S_ISBLK
96 #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
97 #endif
98 #if defined(S_IFLNK) && !defined(S_ISLNK)
99 #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
100 #endif
102 /* Get or fake the disk device blocksize.
103 Usually defined by sys/param.h (if at all). */
104 #ifndef DEV_BSIZE
105 # ifdef BSIZE
106 # define DEV_BSIZE BSIZE
107 # else /* !BSIZE */
108 # define DEV_BSIZE 4096
109 # endif /* !BSIZE */
110 #endif /* !DEV_BSIZE */
112 /* Extract or fake data from a `struct stat'.
113 ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
114 ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
115 ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
116 #ifndef HAVE_STRUCT_STAT_ST_BLOCKS
117 # define ST_BLKSIZE(statbuf) DEV_BSIZE
118 # if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */
119 # define ST_NBLOCKS(statbuf) \
120 (S_ISREG ((statbuf).st_mode) \
121 || S_ISDIR ((statbuf).st_mode) \
122 ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
123 # else /* !_POSIX_SOURCE && BSIZE */
124 # define ST_NBLOCKS(statbuf) \
125 (S_ISREG ((statbuf).st_mode) \
126 || S_ISDIR ((statbuf).st_mode) \
127 ? st_blocks ((statbuf).st_size) : 0)
128 # endif /* !_POSIX_SOURCE && BSIZE */
129 #else /* HAVE_STRUCT_STAT_ST_BLOCKS */
130 /* Some systems, like Sequents, return st_blksize of 0 on pipes. */
131 # define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
132 ? (statbuf).st_blksize : DEV_BSIZE)
133 # if defined(hpux) || defined(__hpux__) || defined(__hpux)
134 /* HP-UX counts st_blocks in 1024-byte units.
135 This loses when mixing HP-UX and BSD filesystems with NFS. */
136 # define ST_NBLOCKSIZE 1024
137 # else /* !hpux */
138 # if defined(_AIX) && defined(_I386)
139 /* AIX PS/2 counts st_blocks in 4K units. */
140 # define ST_NBLOCKSIZE (4 * 1024)
141 # else /* not AIX PS/2 */
142 # if defined(_CRAY)
143 # define ST_NBLOCKS(statbuf) \
144 (S_ISREG ((statbuf).st_mode) \
145 || S_ISDIR ((statbuf).st_mode) \
146 ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
147 # endif /* _CRAY */
148 # endif /* not AIX PS/2 */
149 # endif /* !hpux */
150 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
152 #ifndef ST_NBLOCKS
153 # define ST_NBLOCKS(statbuf) \
154 (S_ISREG ((statbuf).st_mode) \
155 || S_ISDIR ((statbuf).st_mode) \
156 ? (statbuf).st_blocks : 0)
157 #endif
159 #ifndef ST_NBLOCKSIZE
160 # define ST_NBLOCKSIZE 512
161 #endif
163 #ifndef _POSIX_VERSION
164 struct passwd *getpwuid ();
165 struct group *getgrgid ();
166 #endif
168 #ifdef major /* Might be defined in sys/types.h. */
169 #define HAVE_MAJOR
170 #endif
171 #ifndef HAVE_MAJOR
172 #define major(dev) (((dev) >> 8) & 0xff)
173 #define minor(dev) ((dev) & 0xff)
174 #endif
175 #undef HAVE_MAJOR
178 char * get_link_name (char *name, char *relname);
179 static void print_name_with_quoting (register char *p, FILE *stream);
181 extern char * getgroup (gid_t gid);
182 extern char * getuser (uid_t uid);
185 /* NAME is the name to print.
186 RELNAME is the path to access it from the current directory.
187 STATP is the results of stat or lstat on it.
188 Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
189 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
190 and sizes.
191 STREAM is the stdio stream to print on. */
193 void
194 list_file (char *name,
195 char *relname,
196 struct stat *statp,
197 time_t current_time,
198 int output_block_size,
199 FILE *stream)
201 char modebuf[11];
202 struct tm const *when_local;
203 char const *user_name;
204 char const *group_name;
205 char hbuf[LONGEST_HUMAN_READABLE + 1];
207 #if HAVE_ST_DM_MODE
208 /* Cray DMF: look at the file's migrated, not real, status */
209 mode_string (statp->st_dm_mode, modebuf);
210 #else
211 mode_string (statp->st_mode, modebuf);
212 #endif
213 modebuf[10] = '\0';
215 fprintf (stream, "%6s ",
216 human_readable ((uintmax_t) statp->st_ino, hbuf,
217 human_ceiling,
218 1, 1));
220 fprintf (stream, "%4s ",
221 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
222 human_ceiling,
223 ST_NBLOCKSIZE, output_block_size));
226 /* The space between the mode and the number of links is the POSIX
227 "optional alternate access method flag". */
228 fprintf (stream, "%s %3lu ", modebuf, (unsigned long) statp->st_nlink);
230 user_name = getuser (statp->st_uid);
231 if (user_name)
232 fprintf (stream, "%-8s ", user_name);
233 else
234 fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
236 group_name = getgroup (statp->st_gid);
237 if (group_name)
238 fprintf (stream, "%-8s ", group_name);
239 else
240 fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid);
242 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
243 #ifdef HAVE_ST_RDEV
244 fprintf (stream, "%3lu, %3lu ",
245 (unsigned long) major (statp->st_rdev),
246 (unsigned long) minor (statp->st_rdev));
247 #else
248 fprintf (stream, " ");
249 #endif
250 else
251 fprintf (stream, "%8s ",
252 human_readable ((uintmax_t) statp->st_size, hbuf,
253 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 static void
322 print_name_with_quoting (register char *p, FILE *stream)
324 register unsigned char c;
326 while ((c = *p++) != '\0')
328 switch (c)
330 case '\\':
331 fprintf (stream, "\\\\");
332 break;
334 case '\n':
335 fprintf (stream, "\\n");
336 break;
338 case '\b':
339 fprintf (stream, "\\b");
340 break;
342 case '\r':
343 fprintf (stream, "\\r");
344 break;
346 case '\t':
347 fprintf (stream, "\\t");
348 break;
350 case '\f':
351 fprintf (stream, "\\f");
352 break;
354 case ' ':
355 fprintf (stream, "\\ ");
356 break;
358 case '"':
359 fprintf (stream, "\\\"");
360 break;
362 default:
363 if (c > 040 && c < 0177)
364 putc (c, stream);
365 else
366 fprintf (stream, "\\%03o", (unsigned int) c);
371 #ifdef S_ISLNK
372 char *
373 get_link_name (char *name, 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