Imported Upstream version 4.5.14
[platform/upstream/findutils.git] / lib / listfile.c
1 /* listfile.c -- display a long listing of a file
2    Copyright (C) 1991, 1993, 2000, 2004, 2005, 2007, 2008, 2010, 2011
3    Free Software 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 /* config.h must be included first. */
19 #include <config.h>
20
21 /* system headers. */
22 #include <alloca.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <grp.h>
26 #include <locale.h>
27 #include <openat.h>
28 #include <pwd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <time.h>
35 #include <unistd.h> /* for readlink() */
36
37 /* gnulib headers. */
38 #include "areadlink.h"
39 #include "error.h"
40 #include "filemode.h"
41 #include "human.h"
42 #include "idcache.h"
43 #include "pathmax.h"
44 #include "stat-size.h"
45 #include "gettext.h"
46
47 /* find headers. */
48 #include "listfile.h"
49
50 /* Since major is a function on SVR4, we can't use `ifndef major'.  */
51 #ifdef MAJOR_IN_MKDEV
52 #include <sys/mkdev.h>
53 #define HAVE_MAJOR
54 #endif
55 #ifdef MAJOR_IN_SYSMACROS
56 #include <sys/sysmacros.h>
57 #define HAVE_MAJOR
58 #endif
59
60 #ifdef major                    /* Might be defined in sys/types.h.  */
61 #define HAVE_MAJOR
62 #endif
63 #ifndef HAVE_MAJOR
64 #define major(dev)  (((dev) >> 8) & 0xff)
65 #define minor(dev)  ((dev) & 0xff)
66 #endif
67 #undef HAVE_MAJOR
68
69 #if ENABLE_NLS
70 # include <libintl.h>
71 # define _(Text) gettext (Text)
72 #else
73 # define _(Text) Text
74 #endif
75 #ifdef gettext_noop
76 # define N_(String) gettext_noop (String)
77 #else
78 /* See locate.c for explanation as to why not use (String) */
79 # define N_(String) String
80 #endif
81
82 static bool print_name (register const char *p, FILE *stream, int literal_control_chars);
83
84
85 /* NAME is the name to print.
86    RELNAME is the path to access it from the current directory.
87    STATP is the results of stat or lstat on it.
88    Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
89    Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
90    and sizes.
91    STREAM is the stdio stream to print on.  */
92
93 void
94 list_file (const char *name,
95            int dir_fd,
96            char *relname,
97            const struct stat *statp,
98            time_t current_time,
99            int output_block_size,
100            int literal_control_chars,
101            FILE *stream)
102 {
103   char modebuf[12];
104   struct tm const *when_local;
105   char const *user_name;
106   char const *group_name;
107   char hbuf[LONGEST_HUMAN_READABLE + 1];
108   bool output_good = true;
109   int failed_at = 000;
110
111 #if HAVE_ST_DM_MODE
112   /* Cray DMF: look at the file's migrated, not real, status */
113   strmode (statp->st_dm_mode, modebuf);
114 #else
115   strmode (statp->st_mode, modebuf);
116 #endif
117
118   if (fprintf (stream, "%6s ",
119                human_readable ((uintmax_t) statp->st_ino, hbuf,
120                                human_ceiling,
121                                1u, 1u)) < 0)
122     {
123       output_good = false;
124       failed_at = 100;
125     }
126
127   if (output_good)
128     {
129       if (fprintf (stream, "%4s ",
130                    human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
131                                    human_ceiling,
132                                    ST_NBLOCKSIZE, output_block_size)) < 0)
133         {
134           output_good = false;
135           failed_at = 200;
136         }
137     }
138
139   if (output_good)
140     {
141       /* modebuf includes the space between the mode and the number of links,
142          as the POSIX "optional alternate access method flag".  */
143       if (fprintf (stream, "%s%3lu ", modebuf, (unsigned long) statp->st_nlink) < 0)
144         {
145           output_good = false;
146           failed_at = 300;
147         }
148     }
149
150   if (output_good)
151     {
152       user_name = getuser (statp->st_uid);
153       if (user_name)
154         {
155           output_good = (fprintf (stream, "%-8s ", user_name) >= 0);
156           if (!output_good)
157             failed_at = 400;
158         }
159       else
160         {
161           output_good = (fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid) >= 0);
162           if (!output_good)
163             failed_at = 450;
164         }
165     }
166
167   if (output_good)
168     {
169       group_name = getgroup (statp->st_gid);
170       if (group_name)
171         {
172           output_good = (fprintf (stream, "%-8s ", group_name) >= 0);
173           if (!output_good)
174             failed_at = 500;
175         }
176       else
177         {
178           output_good = (fprintf (stream, "%-8lu ", (unsigned long) statp->st_gid) >= 0);
179           if (!output_good)
180             failed_at = 550;
181         }
182     }
183
184   if (output_good)
185     {
186       if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
187         {
188 #ifdef HAVE_STRUCT_STAT_ST_RDEV
189           if (fprintf (stream, "%3lu, %3lu ",
190                        (unsigned long) major (statp->st_rdev),
191                        (unsigned long) minor (statp->st_rdev)) < 0)
192             {
193               output_good = false;
194               failed_at = 600;
195             }
196 #else
197           if (fprintf (stream, "         ") < 0)
198             {
199               output_good = false;
200               failed_at = 700;
201             }
202 #endif
203         }
204       else
205         {
206           if (fprintf (stream, "%8s ",
207                        human_readable ((uintmax_t) statp->st_size, hbuf,
208                                        human_ceiling,
209                                        1,
210                                        output_block_size < 0 ? output_block_size : 1)) < 0)
211             {
212               output_good = false;
213               failed_at = 800;
214             }
215         }
216     }
217
218   if (output_good)
219     {
220       if ((when_local = localtime (&statp->st_mtime)))
221         {
222           char init_bigbuf[256];
223           char *buf = init_bigbuf;
224           size_t bufsize = sizeof init_bigbuf;
225
226           /* Use strftime rather than ctime, because the former can produce
227              locale-dependent names for the month (%b).
228
229              Output the year if the file is fairly old or in the future.
230              POSIX says the cutoff is 6 months old;
231              approximate this by 6*30 days.
232              Allow a 1 hour slop factor for what is considered "the future",
233              to allow for NFS server/client clock disagreement.  */
234           char const *fmt =
235             ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
236               && statp->st_mtime <= current_time + 60 * 60)
237              ? "%b %e %H:%M"
238              : "%b %e  %Y");
239
240           while (!strftime (buf, bufsize, fmt, when_local))
241             buf = alloca (bufsize *= 2);
242
243           if (fprintf (stream, "%s ", buf) < 0)
244             {
245               output_good = false;
246               failed_at = 900;
247             }
248         }
249       else
250         {
251           /* The time cannot be represented as a local time;
252              print it as a huge integer number of seconds.  */
253           int width = 12;
254
255           if (statp->st_mtime < 0)
256             {
257               char const *num = human_readable (- (uintmax_t) statp->st_mtime,
258                                                 hbuf, human_ceiling, 1, 1);
259               int sign_width = width - strlen (num);
260               if (fprintf (stream, "%*s%s ",
261                            sign_width < 0 ? 0 : sign_width, "-", num) < 0)
262                 {
263                   output_good = false;
264                   failed_at = 1000;
265                 }
266             }
267           else
268             {
269               if (fprintf (stream, "%*s ", width,
270                            human_readable ((uintmax_t) statp->st_mtime, hbuf,
271                                            human_ceiling,
272                                            1, 1)) < 0)
273                 {
274                   output_good = false;
275                   failed_at = 1100;
276                 }
277             }
278         }
279     }
280
281   if (output_good)
282     {
283       output_good = print_name (name, stream, literal_control_chars);
284       if (!output_good)
285         {
286           failed_at = 1200;
287         }
288     }
289
290   if (output_good)
291     {
292       if (S_ISLNK (statp->st_mode))
293         {
294           char *linkname = areadlinkat (dir_fd, relname);
295           if (linkname)
296             {
297               if (fputs (" -> ", stream) < 0)
298                 {
299                   output_good = false;
300                   failed_at = 1300;
301                 }
302               if (output_good)
303                 {
304                   output_good = print_name (linkname, stream, literal_control_chars);
305                   if (!output_good)
306                     {
307                       failed_at = 1350;
308                     }
309                 }
310             }
311           else
312             {
313               /* POSIX requires in the case of find that if we issue a
314                * diagnostic we should have a nonzero status.  However,
315                * this function doesn't have a way of telling the caller to
316                * do that.  However, since this function is only used when
317                * processing "-ls", we're already using an extension.
318                */
319               error (0, errno, "%s", name);
320             }
321           free (linkname);
322         }
323       if (output_good)
324         {
325           if (EOF == putc ('\n', stream))
326             {
327               output_good = false;
328               if (!output_good)
329                 {
330                   failed_at = 1400;
331                 }
332             }
333         }
334     }
335   if (!output_good)
336     {
337       error (EXIT_FAILURE, errno, _("Failed to write output (at stage %d)"), failed_at);
338     }
339 }
340
341
342 static bool
343 print_name_without_quoting (const char *p, FILE *stream)
344 {
345   return (fprintf (stream, "%s", p) >= 0);
346 }
347
348
349 static bool
350 print_name_with_quoting (register const char *p, FILE *stream)
351 {
352   register unsigned char c;
353
354   while ((c = *p++) != '\0')
355     {
356       int fprintf_result = -1;
357       switch (c)
358         {
359         case '\\':
360           fprintf_result = fprintf (stream, "\\\\");
361           break;
362
363         case '\n':
364           fprintf_result = fprintf (stream, "\\n");
365           break;
366
367         case '\b':
368           fprintf_result = fprintf (stream, "\\b");
369           break;
370
371         case '\r':
372           fprintf_result = fprintf (stream, "\\r");
373           break;
374
375         case '\t':
376           fprintf_result = fprintf (stream, "\\t");
377           break;
378
379         case '\f':
380           fprintf_result = fprintf (stream, "\\f");
381           break;
382
383         case ' ':
384           fprintf_result = fprintf (stream, "\\ ");
385           break;
386
387         case '"':
388           fprintf_result = fprintf (stream, "\\\"");
389           break;
390
391         default:
392           if (c > 040 && c < 0177)
393             {
394               if (EOF == putc (c, stream))
395                 return false;
396               fprintf_result = 1; /* otherwise it's used uninitialized. */
397             }
398           else
399             {
400               fprintf_result = fprintf (stream, "\\%03o", (unsigned int) c);
401             }
402         }
403       if (fprintf_result < 0)
404         return false;
405     }
406   return true;
407 }
408
409 static bool print_name (register const char *p, FILE *stream, int literal_control_chars)
410 {
411   if (literal_control_chars)
412     return print_name_without_quoting (p, stream);
413   else
414     return print_name_with_quoting (p, stream);
415 }