1 /* listfile.c -- display a long listing of a file
2 Copyright (C) 1991-2022 Free Software Foundation, Inc.
3
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 3 of the License, or
7 (at your option) any later version.
8
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.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 /* config.h must be included first. */
18 #include <config.h>
19
20 /* system headers. */
21 #include <alloca.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <grp.h>
25 #include <pwd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <time.h>
32 #include <unistd.h> /* for readlink() */
33
34 /* gnulib headers. */
35 #include "areadlink.h"
36 #include "error.h"
37 #include "filemode.h"
38 #include "human.h"
39 #include "mbswidth.h"
40 #include "idcache.h"
41 #include "pathmax.h"
42 #include "stat-size.h"
43
44 /* find headers. */
45 #include "system.h"
46 #include "die.h"
47 #include "listfile.h"
48
49 /* Since major is a function on SVR4, we can't use `ifndef major'. */
50 #ifdef MAJOR_IN_MKDEV
51 # include <sys/mkdev.h>
52 #else
53 # ifdef MAJOR_IN_SYSMACROS
54 # include <sys/sysmacros.h>
55 # else
56 # ifndef major /* Might be defined in sys/types.h. */
57 # define major(dev) (((dev) >> 8) & 0xff)
58 # define minor(dev) ((dev) & 0xff)
59 # endif
60 # endif
61 #endif
62
63
64 static bool print_name (register const char *p, FILE *stream, int literal_control_chars);
65
66 /* We have some minimum field sizes, though we try to widen these fields on systems
67 * where we discover examples where the field width we started with is not enough. */
68 static int inode_number_width = 9;
69 static int block_size_width = 6;
70 static int nlink_width = 3;
71 static int owner_width = 8;
72 static int group_width = 8;
73 /* We don't print st_author even if the system has it. */
74 static int major_device_number_width = 3;
75 static int minor_device_number_width = 3;
76 static int file_size_width = 8;
77
78 static bool print_num(FILE *stream, unsigned long num, int *width)
79 {
80 const int chars_out = fprintf (stream, "%*lu", *width, num);
81 if (chars_out >= 0)
82 {
83 if (*width < chars_out)
84 *width = chars_out;
85 return true;
86 }
87 return false;
88 }
89
90
91 /* NAME is the name to print.
92 RELNAME is the path to access it from the current directory.
93 STATP is the results of stat or lstat on it.
94 Use CURRENT_TIME to decide whether to print yyyy or hh:mm.
95 Use OUTPUT_BLOCK_SIZE to determine how to print file block counts
96 and sizes.
97 STREAM is the stdio stream to print on. */
98
99 void
100 list_file (const char *name,
101 int dir_fd,
102 const char *relname,
103 const struct stat *statp,
104 time_t current_time,
105 int output_block_size,
106 int literal_control_chars,
107 FILE *stream)
108 {
109 char modebuf[12];
110 struct tm const *when_local;
111 char const *user_name;
112 char const *group_name;
113 char hbuf[LONGEST_HUMAN_READABLE + 1];
114 bool output_good = true;
115 int chars_out;
116 int failed_at = 000;
117
118 #if HAVE_ST_DM_MODE
119 /* Cray DMF: look at the file's migrated, not real, status */
120 strmode (statp->st_dm_mode, modebuf);
121 #else
122 strmode (statp->st_mode, modebuf);
123 #endif
124
125 chars_out = fprintf (stream, "%*s", inode_number_width,
126 human_readable ((uintmax_t) statp->st_ino, hbuf,
127 human_ceiling,
128 1u, 1u));
129 if (chars_out < 0)
130 {
131 output_good = false;
132 failed_at = 100;
133 }
134 else if (chars_out > inode_number_width)
135 {
136 inode_number_width = chars_out;
137 }
138 if (output_good)
139 {
140 if (EOF == putc(' ', stream))
141 {
142 output_good = false;
143 failed_at = 150;
144 }
145 chars_out = fprintf (stream, "%*s",
146 block_size_width,
147 human_readable ((uintmax_t) ST_NBLOCKS (*statp), hbuf,
148 human_ceiling,
149 ST_NBLOCKSIZE, output_block_size));
150 if (chars_out < 0)
151 {
152 output_good = false;
153 failed_at = 200;
154 }
155 else
156 {
157 if (chars_out > block_size_width)
158 block_size_width = chars_out;
159 }
160 }
161
162 if (output_good)
163 {
164 if (EOF == putc(' ', stream))
165 {
166 output_good = false;
167 failed_at = 250;
168 }
169 }
170 if (output_good)
171 {
172 /* modebuf includes the space between the mode and the number of links,
173 as the POSIX "optional alternate access method flag". */
174 if (fputs (modebuf, stream) < 0)
175 {
176 output_good = false;
177 failed_at = 275;
178 }
179 }
180 if (output_good)
181 {
182 /* This format used to end in a space, but the output of "ls"
183 has only one space between the link count and the owner name,
184 so we removed the trailing space. Happily this also makes it
185 easier to update nlink_width. */
186 chars_out = fprintf (stream, "%*lu",
187 nlink_width, (unsigned long) statp->st_nlink);
188 if (chars_out < 0)
189 {
190 output_good = false;
191 failed_at = 300;
192 }
193 else
194 {
195 if (chars_out > nlink_width)
196 nlink_width = chars_out;
197 }
198 }
199
200 if (output_good)
201 {
202 if (EOF == putc(' ', stream))
203 {
204 output_good = false;
205 failed_at = 250;
206 }
207 user_name = getuser (statp->st_uid);
208 if (user_name)
209 {
210 int len = mbswidth (user_name, 0);
211 if (len > owner_width)
212 owner_width = len;
213 output_good = (fprintf (stream, "%-*s ", owner_width, user_name) >= 0);
214 if (!output_good)
215 failed_at = 400;
216 }
217 else
218 {
219 chars_out = fprintf (stream, "%-8lu ", (unsigned long) statp->st_uid);
220 if (chars_out > owner_width)
221 owner_width = chars_out;
222 output_good = (chars_out > 0);
223 if (!output_good)
224 failed_at = 450;
225 }
226 }
227
228 if (output_good)
229 {
230 group_name = getgroup (statp->st_gid);
231 if (group_name)
232 {
233 int len = mbswidth (group_name, 0);
234 if (len > group_width)
235 group_width = len;
236 output_good = (fprintf (stream, "%-*s ", group_width, group_name) >= 0);
237 if (!output_good)
238 failed_at = 500;
239 }
240 else
241 {
242 chars_out = fprintf (stream, "%-*lu",
243 group_width, (unsigned long) statp->st_gid);
244 if (chars_out > group_width)
245 group_width = chars_out;
246 output_good = (chars_out >= 0);
247 if (output_good)
248 {
249 if (EOF == putc(' ', stream))
250 {
251 output_good = false;
252 failed_at = 525;
253 }
254 }
255 else
256 {
257 if (!output_good)
258 failed_at = 550;
259 }
260 }
261 }
262
263 if (output_good)
264 {
265 if (S_ISCHR (statp->st_mode) || S_ISBLK (statp->st_mode))
266 {
267 #ifdef HAVE_STRUCT_STAT_ST_RDEV
268 if (!print_num (stream,
269 (unsigned long) major (statp->st_rdev),
270 &major_device_number_width))
271 {
272 output_good = false;
273 failed_at = 600;
274 }
275 if (output_good)
276 {
277 if (fprintf (stream, ", ") < 0)
278 {
279 output_good = false;
280 failed_at = 625;
281 }
282 }
283 if (output_good)
284 {
285 if (!print_num (stream,
286 (unsigned long) minor (statp->st_rdev),
287 &minor_device_number_width))
288 {
289 output_good = false;
290 failed_at = 650;
291 }
292 }
293 #else
294 if (fprintf (stream, "%*s %*s",
295 major_device_number_width,
296 minor_device_number_width) < 0)
297 {
298 output_good = false;
299 failed_at = 700;
300 }
301 #endif
302 }
303 else
304 {
305 const int blocksize = output_block_size < 0 ? output_block_size : 1;
306 chars_out = fprintf (stream, "%*s",
307 file_size_width,
308 human_readable ((uintmax_t) statp->st_size, hbuf,
309 human_ceiling,
310 1, blocksize));
311 if (chars_out < 0)
312 {
313 output_good = false;
314 failed_at = 800;
315 }
316 else
317 {
318 if (chars_out > file_size_width)
319 {
320 file_size_width = chars_out;
321 }
322 }
323 }
324 }
325
326 if (output_good)
327 {
328 if (EOF == putc(' ', stream))
329 {
330 output_good = false;
331 failed_at = 850;
332 }
333 }
334
335 if (output_good)
336 {
337 if ((when_local = localtime (&statp->st_mtime)))
338 {
339 char init_bigbuf[256];
340 char *buf = init_bigbuf;
341 size_t bufsize = sizeof init_bigbuf;
342
343 /* Use strftime rather than ctime, because the former can produce
344 locale-dependent names for the month (%b).
345
346 Output the year if the file is fairly old or in the future.
347 POSIX says the cutoff is 6 months old;
348 approximate this by 6*30 days.
349 Allow a 1 hour slop factor for what is considered "the future",
350 to allow for NFS server/client clock disagreement. */
351 char const *fmt =
352 ((current_time - 6 * 30 * 24 * 60 * 60 <= statp->st_mtime
353 && statp->st_mtime <= current_time + 60 * 60)
354 ? "%b %e %H:%M"
355 : "%b %e %Y");
356
357 while (!strftime (buf, bufsize, fmt, when_local))
358 buf = alloca (bufsize *= 2);
359
360 if (fprintf (stream, "%s ", buf) < 0)
361 {
362 output_good = false;
363 failed_at = 900;
364 }
365 }
366 else
367 {
368 /* The time cannot be represented as a local time;
369 print it as a huge integer number of seconds. */
370 int width = 12;
371
372 if (statp->st_mtime < 0)
373 {
374 char const *num = human_readable (- (uintmax_t) statp->st_mtime,
375 hbuf, human_ceiling, 1, 1);
376 int sign_width = width - strlen (num);
377 if (fprintf (stream, "%*s%s ",
378 sign_width < 0 ? 0 : sign_width, "-", num) < 0)
379 {
380 output_good = false;
381 failed_at = 1000;
382 }
383 }
384 else
385 {
386 if (fprintf (stream, "%*s ", width,
387 human_readable ((uintmax_t) statp->st_mtime, hbuf,
388 human_ceiling,
389 1, 1)) < 0)
390 {
391 output_good = false;
392 failed_at = 1100;
393 }
394 }
395 }
396 }
397
398 if (output_good)
399 {
400 output_good = print_name (name, stream, literal_control_chars);
401 if (!output_good)
402 {
403 failed_at = 1200;
404 }
405 }
406
407 if (output_good)
408 {
409 if (S_ISLNK (statp->st_mode))
410 {
411 char *linkname = areadlinkat (dir_fd, relname);
412 if (linkname)
413 {
414 if (fputs (" -> ", stream) < 0)
415 {
416 output_good = false;
417 failed_at = 1300;
418 }
419 if (output_good)
420 {
421 output_good = print_name (linkname, stream, literal_control_chars);
422 if (!output_good)
423 {
424 failed_at = 1350;
425 }
426 }
427 }
428 else
429 {
430 /* POSIX requires in the case of find that if we issue a
431 * diagnostic we should have a nonzero status. However,
432 * this function doesn't have a way of telling the caller to
433 * do that. However, since this function is only used when
434 * processing "-ls", we're already using an extension.
435 */
436 error (0, errno, "%s", name);
437 }
438 free (linkname);
439 }
440 if (output_good)
441 {
442 if (EOF == putc ('\n', stream))
443 {
444 output_good = false;
445 if (!output_good)
446 {
447 failed_at = 1400;
448 }
449 }
450 }
451 }
452 if (!output_good)
453 {
454 die (EXIT_FAILURE, errno, _("Failed to write output (at stage %d)"), failed_at);
455 }
456 }
457
458
459 static bool
460 print_name_without_quoting (const char *p, FILE *stream)
461 {
462 return (fprintf (stream, "%s", p) >= 0);
463 }
464
465
466 static bool
467 print_name_with_quoting (register const char *p, FILE *stream)
468 {
469 register unsigned char c;
470
471 while ((c = *p++) != '\0')
472 {
473 int fprintf_result = -1;
474 switch (c)
475 {
476 case '\\':
477 fprintf_result = fprintf (stream, "\\\\");
478 break;
479
480 case '\n':
481 fprintf_result = fprintf (stream, "\\n");
482 break;
483
484 case '\b':
485 fprintf_result = fprintf (stream, "\\b");
486 break;
487
488 case '\r':
489 fprintf_result = fprintf (stream, "\\r");
490 break;
491
492 case '\t':
493 fprintf_result = fprintf (stream, "\\t");
494 break;
495
496 case '\f':
497 fprintf_result = fprintf (stream, "\\f");
498 break;
499
500 case ' ':
501 fprintf_result = fprintf (stream, "\\ ");
502 break;
503
504 case '"':
505 fprintf_result = fprintf (stream, "\\\"");
506 break;
507
508 default:
509 if (c > 040 && c < 0177)
510 {
511 if (EOF == putc (c, stream))
512 return false;
513 fprintf_result = 1; /* otherwise it's used uninitialized. */
514 }
515 else
516 {
517 fprintf_result = fprintf (stream, "\\%03o", (unsigned int) c);
518 }
519 }
520 if (fprintf_result < 0)
521 return false;
522 }
523 return true;
524 }
525
526 static bool print_name (register const char *p, FILE *stream, int literal_control_chars)
527 {
528 if (literal_control_chars)
529 return print_name_without_quoting (p, stream);
530 else
531 return print_name_with_quoting (p, stream);
532 }