1 /* $NetBSD: stat.c,v 1.55 2025/05/15 19:11:44 nia Exp $ */ 2 3 /* 4 * Copyright (c) 2002-2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Brown. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 /* config checked libc, we need the prototype as well */ 35 #undef HAVE_DEVNAME 36 #endif 37 38 #include <sys/cdefs.h> 39 #if !defined(lint) 40 __RCSID("$NetBSD: stat.c,v 1.55 2025/05/15 19:11:44 nia Exp $"); 41 #endif 42 43 #if ! HAVE_NBTOOL_CONFIG_H 44 #define HAVE_STRUCT_STAT_ST_FLAGS 1 45 #define HAVE_STRUCT_STAT_ST_GEN 1 46 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 47 #define HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC 1 48 #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 49 #define HAVE_DEVNAME 1 50 #endif /* HAVE_NBTOOL_CONFIG_H */ 51 52 #include <sys/types.h> 53 #include <sys/stat.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <grp.h> 59 #include <limits.h> 60 #include <pwd.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <time.h> 65 #include <unistd.h> 66 #if HAVE_STRUCT_STAT_ST_FLAGS && !HAVE_NBTOOL_CONFIG_H 67 #include <util.h> 68 #endif 69 #include <vis.h> 70 71 #if HAVE_STRUCT_STAT_ST_FLAGS 72 #define DEF_F "%#Xf " 73 #define RAW_F "%f " 74 #define SHELL_F " st_flags=%f" 75 #else /* HAVE_STRUCT_STAT_ST_FLAGS */ 76 #define DEF_F 77 #define RAW_F 78 #define SHELL_F 79 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */ 80 81 #if HAVE_STRUCT_STAT_ST_BIRTHTIME 82 #define DEF_B "\"%SB\" " 83 #define RAW_B "%B " 84 #define SHELL_B "st_birthtime=%SB " 85 #define LINUX_B "%n Birth: %SB" 86 #else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 87 #define DEF_B 88 #define RAW_B 89 #define SHELL_B 90 #define LINUX_B 91 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 92 93 #if HAVE_STRUCT_STAT_ST_ATIM 94 #define st_atimespec st_atim 95 #define st_ctimespec st_ctim 96 #define st_mtimespec st_mtim 97 #endif /* HAVE_STRUCT_STAT_ST_ATIM */ 98 99 #define DEF_FORMAT \ 100 "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ 101 "%k %b " DEF_F "%N" 102 #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ 103 "%k %b " RAW_F "%N" 104 #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" 105 #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" 106 #define SHELL_FORMAT \ 107 "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ 108 "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ 109 "st_atime=%Sa st_mtime=%Sm st_ctime=%Sc " SHELL_B \ 110 "st_blksize=%k st_blocks=%b" SHELL_F 111 #define LINUX_FORMAT \ 112 " File: \"%N\"%n" \ 113 " Size: %-11z Blocks: %-11b IO Block: %-11k %HT%n" \ 114 "Device: %Hd,%Ld Inode: %i Links: %l%n" \ 115 " Mode: (%Mp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ 116 "Access: %Sa%n" \ 117 "Modify: %Sm%n" \ 118 "Change: %Sc" \ 119 LINUX_B 120 121 #define TIME_FORMAT "%b %e %T %Y" 122 123 #define FLAG_POUND 0x01 124 #define FLAG_SPACE 0x02 125 #define FLAG_PLUS 0x04 126 #define FLAG_ZERO 0x08 127 #define FLAG_MINUS 0x10 128 129 /* 130 * These format characters must all be unique, except the magic one. 131 */ 132 #define FMT_MAGIC '%' 133 #define FMT_DOT '.' 134 135 #define SIMPLE_NEWLINE 'n' 136 #define SIMPLE_TAB 't' 137 #define SIMPLE_PERCENT '%' 138 #define SIMPLE_NUMBER '@' 139 140 #define FMT_POUND '#' 141 #define FMT_SPACE ' ' 142 #define FMT_PLUS '+' 143 #define FMT_ZERO '0' 144 #define FMT_MINUS '-' 145 146 #define FMT_DECIMAL 'D' 147 #define FMT_OCTAL 'O' 148 #define FMT_UNSIGNED 'U' 149 #define FMT_HEX 'X' 150 #define FMT_FLOAT 'F' 151 #define FMT_STRING 'S' 152 153 #define FMTF_DECIMAL 0x01 154 #define FMTF_OCTAL 0x02 155 #define FMTF_UNSIGNED 0x04 156 #define FMTF_HEX 0x08 157 #define FMTF_FLOAT 0x10 158 #define FMTF_STRING 0x20 159 160 #define HIGH_PIECE 'H' 161 #define MIDDLE_PIECE 'M' 162 #define LOW_PIECE 'L' 163 164 #define SHOW_realpath 'R' 165 #define SHOW_st_dev 'd' 166 #define SHOW_st_ino 'i' 167 #define SHOW_st_mode 'p' 168 #define SHOW_st_nlink 'l' 169 #define SHOW_st_uid 'u' 170 #define SHOW_st_gid 'g' 171 #define SHOW_st_rdev 'r' 172 #define SHOW_st_atime 'a' 173 #define SHOW_st_mtime 'm' 174 #define SHOW_st_ctime 'c' 175 #define SHOW_st_btime 'B' 176 #define SHOW_st_size 'z' 177 #define SHOW_st_blocks 'b' 178 #define SHOW_st_blksize 'k' 179 #define SHOW_st_flags 'f' 180 #define SHOW_st_gen 'v' 181 #define SHOW_symlink 'Y' 182 #define SHOW_filetype 'T' 183 #define SHOW_filename 'N' 184 #define SHOW_sizerdev 'Z' 185 186 static void usage(const char *) __dead; 187 static void output(const struct stat *, const char *, 188 const char *, int, int, int); 189 static int format1(const struct stat *, /* stat info */ 190 const char *, /* the file name */ 191 const char *, int, /* the format string itself */ 192 char *, size_t, /* a place to put the output */ 193 int, int, int, int, /* the parsed format */ 194 int, int, int); 195 196 static const char *timefmt; 197 static int linkfail; 198 199 #define addchar(s, c, nl) \ 200 do { \ 201 (void)fputc((c), (s)); \ 202 (*nl) = ((c) == '\n'); \ 203 } while (0/*CONSTCOND*/) 204 205 int 206 main(int argc, char *argv[]) 207 { 208 struct stat st; 209 int ch, rc, errs, am_readlink; 210 int lsF, fmtchar, usestat, fn, nonl, quiet; 211 const char *statfmt, *options, *synopsis; 212 213 am_readlink = 0; 214 lsF = 0; 215 fmtchar = '\0'; 216 usestat = 0; 217 nonl = 0; 218 quiet = 0; 219 linkfail = 0; 220 statfmt = NULL; 221 timefmt = NULL; 222 223 setprogname(argv[0]); 224 225 if (strcmp(getprogname(), "readlink") == 0) { 226 am_readlink = 1; 227 options = "fnqsv"; 228 synopsis = "[-fnqsv] file ..."; 229 statfmt = "%Y"; 230 fmtchar = 'f'; 231 if (getenv("POSIXLY_CORRECT") == NULL) 232 quiet = 1; 233 } else { 234 options = "f:FlLnqrst:x"; 235 synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; 236 } 237 238 while ((ch = getopt(argc, argv, options)) != -1) 239 switch (ch) { 240 case 'F': 241 lsF = 1; 242 break; 243 case 'L': 244 usestat = 1; 245 break; 246 case 'n': 247 nonl = 1; 248 break; 249 case 'q': 250 quiet = 1; 251 break; 252 case 'f': 253 if (am_readlink) { 254 statfmt = "%R"; 255 break; 256 } 257 statfmt = optarg; 258 /* FALLTHROUGH */ 259 case 'l': 260 case 'r': 261 case 's': 262 if (am_readlink) { 263 quiet = 1; 264 break; 265 } 266 /*FALLTHROUGH*/ 267 case 'x': 268 if (fmtchar != 0) 269 errx(1, "can't use format '%c' with '%c'", 270 fmtchar, ch); 271 fmtchar = ch; 272 break; 273 case 't': 274 timefmt = optarg; 275 break; 276 case 'v': 277 quiet = 0; 278 break; 279 default: 280 usage(synopsis); 281 } 282 283 argc -= optind; 284 argv += optind; 285 fn = 1; 286 287 if (am_readlink && argc == 0) 288 usage(synopsis); 289 290 if (fmtchar == '\0') { 291 if (lsF) 292 fmtchar = 'l'; 293 else { 294 fmtchar = 'f'; 295 statfmt = DEF_FORMAT; 296 } 297 } 298 299 if (lsF && fmtchar != 'l') 300 errx(1, "can't use format '%c' with -F", fmtchar); 301 302 switch (fmtchar) { 303 case 'f': 304 /* statfmt already set */ 305 break; 306 case 'l': 307 statfmt = lsF ? LSF_FORMAT : LS_FORMAT; 308 break; 309 case 'r': 310 statfmt = RAW_FORMAT; 311 break; 312 case 's': 313 statfmt = SHELL_FORMAT; 314 if (timefmt == NULL) 315 timefmt = "%s"; 316 break; 317 case 'x': 318 statfmt = LINUX_FORMAT; 319 if (timefmt == NULL) 320 timefmt = "%Y-%m-%d %H:%M:%S.%f %z"; 321 break; 322 default: 323 usage(synopsis); 324 /*NOTREACHED*/ 325 } 326 327 if (timefmt == NULL) 328 timefmt = TIME_FORMAT; 329 330 errs = 0; 331 do { 332 if (argc == 0) { 333 fn = 0; 334 rc = fstat(STDIN_FILENO, &st); 335 } else if (usestat) { 336 /* 337 * Try stat() and if it fails, fall back to 338 * lstat() just in case we're examining a 339 * broken symlink. 340 */ 341 if ((rc = stat(argv[0], &st)) == -1 && 342 errno == ENOENT && 343 (rc = lstat(argv[0], &st)) == -1) 344 errno = ENOENT; 345 } else 346 rc = lstat(argv[0], &st); 347 348 if (rc == -1) { 349 errs = 1; 350 linkfail = 1; 351 if (!quiet) { 352 if (argc == 0) 353 warn("%s: %s", 354 "(stdin)", "fstat"); 355 else 356 warn("%s: %s", argv[0], 357 usestat ? "stat" : "lstat"); 358 } 359 } else if (am_readlink && statfmt[1] == 'Y' && 360 (st.st_mode & S_IFMT) != S_IFLNK) { 361 linkfail = 1; 362 if (!quiet) 363 warnx("%s: Not a symbolic link", argv[0]); 364 } else 365 output(&st, argv[0], statfmt, fn, nonl, quiet); 366 367 argv++; 368 argc--; 369 fn++; 370 } while (argc > 0); 371 372 return (am_readlink ? linkfail : errs); 373 } 374 375 static void 376 usage(const char *synopsis) 377 { 378 379 (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); 380 exit(1); 381 } 382 383 /* 384 * Parses a format string. 385 */ 386 static void 387 output(const struct stat *st, const char *file, 388 const char *statfmt, int fn, int nonl, int quiet) 389 { 390 int flags, size, prec, ofmt, hilo, what; 391 /* 392 * buf size is enough for an item of length PATH_MAX, 393 * multiplied by 4 for vis encoding, plus 4 for symlink 394 * " -> " prefix, plus 1 for \0 terminator. 395 */ 396 char buf[PATH_MAX * 4 + 4 + 1]; 397 const char *subfmt; 398 int nl, t, i; 399 400 nl = 1; 401 while (*statfmt != '\0') { 402 403 /* 404 * Non-format characters go straight out. 405 */ 406 if (*statfmt != FMT_MAGIC) { 407 addchar(stdout, *statfmt, &nl); 408 statfmt++; 409 continue; 410 } 411 412 /* 413 * The current format "substring" starts here, 414 * and then we skip the magic. 415 */ 416 subfmt = statfmt; 417 statfmt++; 418 419 /* 420 * Some simple one-character "formats". 421 */ 422 switch (*statfmt) { 423 case SIMPLE_NEWLINE: 424 addchar(stdout, '\n', &nl); 425 statfmt++; 426 continue; 427 case SIMPLE_TAB: 428 addchar(stdout, '\t', &nl); 429 statfmt++; 430 continue; 431 case SIMPLE_PERCENT: 432 addchar(stdout, '%', &nl); 433 statfmt++; 434 continue; 435 case SIMPLE_NUMBER: { 436 char num[12], *p; 437 438 snprintf(num, sizeof(num), "%d", fn); 439 for (p = &num[0]; *p; p++) 440 addchar(stdout, *p, &nl); 441 statfmt++; 442 continue; 443 } 444 } 445 446 /* 447 * This must be an actual format string. Format strings are 448 * similar to printf(3) formats up to a point, and are of 449 * the form: 450 * 451 * % required start of format 452 * [-# +0] opt. format characters 453 * size opt. field width 454 * . opt. decimal separator, followed by 455 * prec opt. precision 456 * fmt opt. output specifier (string, numeric, etc.) 457 * sub opt. sub field specifier (high, middle, low) 458 * datum required field specifier (size, mode, etc) 459 * 460 * Only the % and the datum selector are required. All data 461 * have reasonable default output forms. The "sub" specifier 462 * only applies to certain data (mode, dev, rdev, filetype). 463 * The symlink output defaults to STRING, yet will only emit 464 * the leading " -> " if STRING is explicitly specified. The 465 * sizerdev datum will generate rdev output for character or 466 * block devices, and size output for all others. 467 * For STRING output, the # format requests vis encoding. 468 */ 469 flags = 0; 470 do { 471 if (*statfmt == FMT_POUND) 472 flags |= FLAG_POUND; 473 else if (*statfmt == FMT_SPACE) 474 flags |= FLAG_SPACE; 475 else if (*statfmt == FMT_PLUS) 476 flags |= FLAG_PLUS; 477 else if (*statfmt == FMT_ZERO) 478 flags |= FLAG_ZERO; 479 else if (*statfmt == FMT_MINUS) 480 flags |= FLAG_MINUS; 481 else 482 break; 483 statfmt++; 484 } while (1/*CONSTCOND*/); 485 486 size = -1; 487 if (isdigit((unsigned char)*statfmt)) { 488 size = 0; 489 while (isdigit((unsigned char)*statfmt)) { 490 size = (size * 10) + (*statfmt - '0'); 491 statfmt++; 492 if (size < 0) 493 goto badfmt; 494 } 495 } 496 497 prec = -1; 498 if (*statfmt == FMT_DOT) { 499 statfmt++; 500 501 prec = 0; 502 while (isdigit((unsigned char)*statfmt)) { 503 prec = (prec * 10) + (*statfmt - '0'); 504 statfmt++; 505 if (prec < 0) 506 goto badfmt; 507 } 508 } 509 510 #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break 511 #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break 512 switch (*statfmt) { 513 fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); 514 fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); 515 fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); 516 fmtcasef(ofmt, FMT_HEX, FMTF_HEX); 517 fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); 518 fmtcasef(ofmt, FMT_STRING, FMTF_STRING); 519 default: 520 ofmt = 0; 521 break; 522 } 523 524 switch (*statfmt) { 525 fmtcase(hilo, HIGH_PIECE); 526 fmtcase(hilo, MIDDLE_PIECE); 527 fmtcase(hilo, LOW_PIECE); 528 default: 529 hilo = 0; 530 break; 531 } 532 533 switch (*statfmt) { 534 fmtcase(what, SHOW_realpath); 535 fmtcase(what, SHOW_st_dev); 536 fmtcase(what, SHOW_st_ino); 537 fmtcase(what, SHOW_st_mode); 538 fmtcase(what, SHOW_st_nlink); 539 fmtcase(what, SHOW_st_uid); 540 fmtcase(what, SHOW_st_gid); 541 fmtcase(what, SHOW_st_rdev); 542 fmtcase(what, SHOW_st_atime); 543 fmtcase(what, SHOW_st_mtime); 544 fmtcase(what, SHOW_st_ctime); 545 fmtcase(what, SHOW_st_btime); 546 fmtcase(what, SHOW_st_size); 547 fmtcase(what, SHOW_st_blocks); 548 fmtcase(what, SHOW_st_blksize); 549 fmtcase(what, SHOW_st_flags); 550 fmtcase(what, SHOW_st_gen); 551 fmtcase(what, SHOW_symlink); 552 fmtcase(what, SHOW_filetype); 553 fmtcase(what, SHOW_filename); 554 fmtcase(what, SHOW_sizerdev); 555 default: 556 goto badfmt; 557 } 558 #undef fmtcasef 559 #undef fmtcase 560 561 t = format1(st, 562 file, 563 subfmt, statfmt - subfmt, 564 buf, sizeof(buf), 565 flags, size, prec, ofmt, hilo, what, quiet); 566 567 for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) 568 addchar(stdout, buf[i], &nl); 569 570 continue; 571 572 badfmt: 573 errx(1, "%.*s: bad format", 574 (int)(statfmt - subfmt + 1), subfmt); 575 } 576 577 if (!nl && !nonl) 578 (void)fputc('\n', stdout); 579 (void)fflush(stdout); 580 } 581 582 static const char * 583 fmttime(char *buf, size_t len, const char *fmt, time_t secs, long nsecs) 584 { 585 struct tm tm; 586 const char *fpb, *fp1, *fp2; /* pointers into fmt */ 587 char *fmt2 = NULL; /* replacement fmt (if not NULL) */ 588 /* XXX init of next twp for stupid gcc only */ 589 char *f2p = NULL; /* ptr into fmt2 - last added */ 590 size_t flen = 0; 591 size_t o; 592 int sl; 593 594 if (localtime_r(&secs, &tm) == NULL) { 595 secs = 0; 596 (void)localtime_r(&secs, &tm); 597 } 598 for (fp1 = fpb = fmt; (fp2 = strchr(fp1, '%')) != NULL; ) { 599 if (fp2[1] != 'f') { 600 /* make sure we don't find the 2nd '%' in "%%" */ 601 fp1 = fp2 + 1 + (fp2[1] != '\0'); 602 continue; 603 } 604 if (fmt2 == NULL) { 605 /* allow for ~100 %f's in the format ... */ 606 flen = strlen(fmt) + 1024; 607 608 if ((fmt2 = calloc(flen, 1)) == NULL) { 609 fp1 = fp2 + 2; 610 continue; 611 } 612 f2p = fmt2; 613 614 o = (size_t)(fp2 - fpb); 615 memcpy(f2p, fpb, o); /* must fit */ 616 fmt = fmt2; 617 } else { 618 o = (size_t)(fp2 - fpb); 619 if (flen > o) 620 memcpy(f2p, fpb, o); 621 } 622 if (flen < o + 10) { /* 9 digits + \0 == 10 */ 623 *f2p = '\0'; 624 break; 625 } 626 f2p += o; 627 flen -= o; 628 sl = snprintf(f2p, flen, "%.9ld", nsecs); 629 if (sl == -1) 630 sl = 0; 631 f2p += sl; 632 *f2p = '\0'; 633 flen -= sl; 634 fp1 = fp2 + 2; 635 fpb = fp1; 636 } 637 if (fmt2 != NULL) { 638 o = strlen(fpb); 639 if (flen > o) { 640 memcpy(f2p, fpb, o); 641 f2p[o] = '\0'; 642 } 643 } 644 645 (void)strftime(buf, len, fmt, &tm); 646 free(fmt2); 647 return buf; 648 } 649 650 /* 651 * Arranges output according to a single parsed format substring. 652 */ 653 static int 654 format1(const struct stat *st, 655 const char *file, 656 const char *fmt, int flen, 657 char *buf, size_t blen, 658 int flags, int size, int prec, int ofmt, 659 int hilo, int what, int quiet) 660 { 661 uint64_t data; 662 char *stmp, lfmt[24], tmp[20]; 663 const char *sdata; 664 char smode[12], sid[13], path[PATH_MAX + 4], visbuf[PATH_MAX * 4 + 4]; 665 struct passwd *pw; 666 struct group *gr; 667 time_t secs; 668 long nsecs; 669 int l; 670 int formats; /* bitmap of allowed formats for this datum */ 671 int small; /* true if datum is a small integer */ 672 int gottime; /* true if secs and nsecs are valid */ 673 int shift; /* powers of 2 to scale numbers before printing */ 674 size_t prefixlen; /* length of constant prefix for string data */ 675 676 formats = 0; 677 small = 0; 678 gottime = 0; 679 secs = 0; 680 nsecs = 0; 681 shift = 0; 682 prefixlen = 0; 683 684 /* 685 * First, pick out the data and tweak it based on hilo or 686 * specified output format (symlink output only). 687 */ 688 switch (what) { 689 case SHOW_st_dev: 690 case SHOW_st_rdev: 691 small = (sizeof(st->st_dev) == 4); 692 data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; 693 #if HAVE_DEVNAME 694 sdata = (what == SHOW_st_dev) ? 695 devname(st->st_dev, S_IFBLK) : 696 devname(st->st_rdev, 697 S_ISCHR(st->st_mode) ? S_IFCHR : 698 S_ISBLK(st->st_mode) ? S_IFBLK : 699 0U); 700 if (sdata == NULL) 701 sdata = "???"; 702 #endif /* HAVE_DEVNAME */ 703 if (hilo == HIGH_PIECE) { 704 data = major(data); 705 hilo = 0; 706 } 707 else if (hilo == LOW_PIECE) { 708 data = minor((unsigned)data); 709 hilo = 0; 710 } 711 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 712 #if HAVE_DEVNAME 713 FMTF_STRING; 714 #else /* HAVE_DEVNAME */ 715 0; 716 #endif /* HAVE_DEVNAME */ 717 if (ofmt == 0) { 718 if (data == (uint64_t)-1) 719 ofmt = FMTF_DECIMAL; 720 else 721 ofmt = FMTF_UNSIGNED; 722 } 723 break; 724 case SHOW_st_ino: 725 small = (sizeof(st->st_ino) == 4); 726 data = st->st_ino; 727 sdata = NULL; 728 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 729 if (ofmt == 0) 730 ofmt = FMTF_UNSIGNED; 731 break; 732 case SHOW_st_mode: 733 small = (sizeof(st->st_mode) == 4); 734 data = st->st_mode; 735 strmode(st->st_mode, smode); 736 stmp = smode; 737 l = strlen(stmp); 738 if (stmp[l - 1] == ' ') 739 stmp[--l] = '\0'; 740 if (hilo == HIGH_PIECE) { 741 data >>= 12; 742 stmp += 1; 743 stmp[3] = '\0'; 744 hilo = 0; 745 } 746 else if (hilo == MIDDLE_PIECE) { 747 data = (data >> 9) & 07; 748 stmp += 4; 749 stmp[3] = '\0'; 750 hilo = 0; 751 } 752 else if (hilo == LOW_PIECE) { 753 data &= 0777; 754 stmp += 7; 755 stmp[3] = '\0'; 756 hilo = 0; 757 } 758 sdata = stmp; 759 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 760 FMTF_STRING; 761 if (ofmt == 0) 762 ofmt = FMTF_OCTAL; 763 break; 764 case SHOW_st_nlink: 765 small = (sizeof(st->st_dev) == 4); 766 data = st->st_nlink; 767 sdata = NULL; 768 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 769 if (ofmt == 0) 770 ofmt = FMTF_UNSIGNED; 771 break; 772 case SHOW_st_uid: 773 small = (sizeof(st->st_uid) == 4); 774 data = st->st_uid; 775 if ((pw = getpwuid(st->st_uid)) != NULL) 776 sdata = pw->pw_name; 777 else { 778 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); 779 sdata = sid; 780 } 781 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 782 FMTF_STRING; 783 if (ofmt == 0) 784 ofmt = FMTF_UNSIGNED; 785 break; 786 case SHOW_st_gid: 787 small = (sizeof(st->st_gid) == 4); 788 data = st->st_gid; 789 if ((gr = getgrgid(st->st_gid)) != NULL) 790 sdata = gr->gr_name; 791 else { 792 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); 793 sdata = sid; 794 } 795 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 796 FMTF_STRING; 797 if (ofmt == 0) 798 ofmt = FMTF_UNSIGNED; 799 break; 800 case SHOW_st_atime: 801 gottime = 1; 802 secs = st->st_atime; 803 #if HAVE_STRUCT_STAT_ST_MTIMENSEC 804 nsecs = st->st_atimensec; 805 #endif 806 /* FALLTHROUGH */ 807 case SHOW_st_mtime: 808 if (!gottime) { 809 gottime = 1; 810 secs = st->st_mtime; 811 #if HAVE_STRUCT_STAT_ST_MTIMENSEC 812 nsecs = st->st_mtimensec; 813 #endif 814 } 815 /* FALLTHROUGH */ 816 case SHOW_st_ctime: 817 if (!gottime) { 818 gottime = 1; 819 secs = st->st_ctime; 820 #if HAVE_STRUCT_STAT_ST_MTIMENSEC 821 nsecs = st->st_ctimensec; 822 #endif 823 } 824 #if HAVE_STRUCT_STAT_ST_BIRTHTIME 825 /* FALLTHROUGH */ 826 case SHOW_st_btime: 827 if (!gottime) { 828 gottime = 1; 829 secs = st->st_birthtime; 830 #if HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC 831 nsecs = st->st_birthtimensec; 832 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC */ 833 } 834 #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 835 small = (sizeof(secs) == 4); 836 data = secs; 837 sdata = fmttime(path, sizeof(path), timefmt, secs, nsecs); 838 839 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 840 FMTF_FLOAT | FMTF_STRING; 841 if (ofmt == 0) 842 ofmt = FMTF_DECIMAL; 843 break; 844 case SHOW_st_size: 845 small = (sizeof(st->st_size) == 4); 846 data = st->st_size; 847 sdata = NULL; 848 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 849 if (ofmt == 0) 850 ofmt = FMTF_UNSIGNED; 851 switch (hilo) { 852 case HIGH_PIECE: 853 shift = 30; /* gigabytes */ 854 hilo = 0; 855 break; 856 case MIDDLE_PIECE: 857 shift = 20; /* megabytes */ 858 hilo = 0; 859 break; 860 case LOW_PIECE: 861 shift = 10; /* kilobytes */ 862 hilo = 0; 863 break; 864 } 865 break; 866 case SHOW_st_blocks: 867 small = (sizeof(st->st_blocks) == 4); 868 data = st->st_blocks; 869 sdata = NULL; 870 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 871 if (ofmt == 0) 872 ofmt = FMTF_UNSIGNED; 873 break; 874 case SHOW_st_blksize: 875 small = (sizeof(st->st_blksize) == 4); 876 data = st->st_blksize; 877 sdata = NULL; 878 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 879 if (ofmt == 0) 880 ofmt = FMTF_UNSIGNED; 881 break; 882 #if HAVE_STRUCT_STAT_ST_FLAGS 883 case SHOW_st_flags: 884 small = (sizeof(st->st_flags) == 4); 885 data = st->st_flags; 886 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 887 #if !HAVE_NBTOOL_CONFIG_H 888 sdata = flags_to_string((u_long)st->st_flags, "-"); 889 formats |= FMT_STRING; 890 #endif 891 if (ofmt == 0) 892 ofmt = FMTF_UNSIGNED; 893 break; 894 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */ 895 #if HAVE_STRUCT_STAT_ST_GEN 896 case SHOW_st_gen: 897 small = (sizeof(st->st_gen) == 4); 898 data = st->st_gen; 899 sdata = NULL; 900 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 901 if (ofmt == 0) 902 ofmt = FMTF_UNSIGNED; 903 break; 904 #endif /* HAVE_STRUCT_STAT_ST_GEN */ 905 case SHOW_realpath: 906 small = 0; 907 data = 0; 908 if (file == NULL) { 909 (void)strlcpy(path, "(stdin)", sizeof(path)); 910 sdata = path; 911 } else { 912 snprintf(path, sizeof(path), " -> "); 913 if (realpath(file, path + 4) == NULL) { 914 if (!quiet) 915 warn("realpath `%s'", file); 916 linkfail = 1; 917 l = 0; 918 path[0] = '\0'; 919 } 920 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 921 prefixlen = (ofmt == FMTF_STRING ? 4 : 0); 922 } 923 924 formats = FMTF_STRING; 925 if (ofmt == 0) 926 ofmt = FMTF_STRING; 927 break; 928 case SHOW_symlink: 929 small = 0; 930 data = 0; 931 if (S_ISLNK(st->st_mode)) { 932 snprintf(path, sizeof(path), " -> "); 933 l = readlink(file, path + 4, sizeof(path) - 4 - 1); 934 if (l == -1) { 935 if (!quiet) 936 warn("readlink `%s'", file); 937 linkfail = 1; 938 l = 0; 939 path[0] = '\0'; 940 } 941 path[l + 4] = '\0'; 942 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 943 prefixlen = (ofmt == FMTF_STRING ? 4 : 0); 944 } 945 else { 946 linkfail = 1; 947 sdata = ""; 948 } 949 formats = FMTF_STRING; 950 if (ofmt == 0) 951 ofmt = FMTF_STRING; 952 break; 953 case SHOW_filetype: 954 small = 0; 955 data = 0; 956 sdata = ""; 957 if (hilo == 0 || hilo == LOW_PIECE) { 958 switch (st->st_mode & S_IFMT) { 959 case S_IFIFO: sdata = "|"; break; 960 case S_IFDIR: sdata = "/"; break; 961 case S_IFREG: 962 if (st->st_mode & 963 (S_IXUSR | S_IXGRP | S_IXOTH)) 964 sdata = "*"; 965 break; 966 case S_IFLNK: sdata = "@"; break; 967 #ifdef S_IFSOCK 968 case S_IFSOCK: sdata = "="; break; 969 #endif 970 #ifdef S_IFWHT 971 case S_IFWHT: sdata = "%"; break; 972 #endif /* S_IFWHT */ 973 #ifdef S_IFDOOR 974 case S_IFDOOR: sdata = ">"; break; 975 #endif /* S_IFDOOR */ 976 } 977 hilo = 0; 978 } 979 else if (hilo == HIGH_PIECE) { 980 switch (st->st_mode & S_IFMT) { 981 case S_IFIFO: sdata = "Fifo File"; break; 982 case S_IFCHR: sdata = "Character Device"; break; 983 case S_IFDIR: sdata = "Directory"; break; 984 case S_IFBLK: sdata = "Block Device"; break; 985 case S_IFREG: sdata = "Regular File"; break; 986 case S_IFLNK: sdata = "Symbolic Link"; break; 987 #ifdef S_IFSOCK 988 case S_IFSOCK: sdata = "Socket"; break; 989 #endif 990 #ifdef S_IFWHT 991 case S_IFWHT: sdata = "Whiteout File"; break; 992 #endif /* S_IFWHT */ 993 #ifdef S_IFDOOR 994 case S_IFDOOR: sdata = "Door"; break; 995 #endif /* S_IFDOOR */ 996 default: sdata = "???"; break; 997 } 998 hilo = 0; 999 } 1000 formats = FMTF_STRING; 1001 if (ofmt == 0) 1002 ofmt = FMTF_STRING; 1003 break; 1004 case SHOW_filename: 1005 small = 0; 1006 data = 0; 1007 if (file == NULL) { 1008 (void)strlcpy(path, "(stdin)", sizeof(path)); 1009 if (hilo == HIGH_PIECE || hilo == LOW_PIECE) 1010 hilo = 0; 1011 } 1012 else if (hilo == 0) 1013 (void)strlcpy(path, file, sizeof(path)); 1014 else { 1015 char *s; 1016 (void)strlcpy(path, file, sizeof(path)); 1017 s = strrchr(path, '/'); 1018 if (s != NULL) { 1019 /* trim off trailing /'s */ 1020 while (s != path && 1021 s[0] == '/' && s[1] == '\0') 1022 *s-- = '\0'; 1023 s = strrchr(path, '/'); 1024 } 1025 if (hilo == HIGH_PIECE) { 1026 if (s == NULL) 1027 (void)strlcpy(path, ".", sizeof(path)); 1028 else { 1029 while (s != path && s[0] == '/') 1030 *s-- = '\0'; 1031 } 1032 hilo = 0; 1033 } 1034 else if (hilo == LOW_PIECE) { 1035 if (s != NULL && s[1] != '\0') 1036 (void)strlcpy(path, s + 1, 1037 sizeof(path)); 1038 hilo = 0; 1039 } 1040 } 1041 sdata = path; 1042 formats = FMTF_STRING; 1043 if (ofmt == 0) 1044 ofmt = FMTF_STRING; 1045 break; 1046 case SHOW_sizerdev: 1047 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 1048 char majdev[20], mindev[20]; 1049 int l1, l2; 1050 1051 if (size == 0) /* avoid -1/2 */ 1052 size++; /* 1/2 == 0/2 so this is safe */ 1053 l1 = format1(st, 1054 file, 1055 fmt, flen, 1056 majdev, sizeof(majdev), 1057 flags, (size - 1) / 2, prec, 1058 ofmt, HIGH_PIECE, SHOW_st_rdev, quiet); 1059 l2 = format1(st, 1060 file, 1061 fmt, flen, 1062 mindev, sizeof(mindev), 1063 flags | FLAG_MINUS , size / 2, prec, 1064 ofmt, LOW_PIECE, SHOW_st_rdev, quiet); 1065 return (snprintf(buf, blen, "%.*s,%.*s", 1066 l1, majdev, l2, mindev)); 1067 } 1068 else { 1069 return (format1(st, 1070 file, 1071 fmt, flen, 1072 buf, blen, 1073 flags, size, prec, 1074 ofmt, 0, SHOW_st_size, quiet)); 1075 } 1076 /*NOTREACHED*/ 1077 default: 1078 errx(1, "%.*s: bad format", (int)flen, fmt); 1079 } 1080 1081 if (hilo != 0 // subdatum not supported 1082 || !(ofmt & formats) // output format not supported 1083 || (ofmt == FMTF_STRING && flags & FLAG_SPACE) 1084 || (ofmt == FMTF_STRING && flags & FLAG_PLUS) 1085 || (ofmt == FMTF_STRING && flags & FLAG_ZERO)) 1086 errx(1, "%.*s: bad format", (int)flen, fmt); 1087 1088 /* 1089 * FLAG_POUND with FMTF_STRING means use vis(3) encoding. 1090 * First prefixlen chars are not encoded. 1091 */ 1092 if ((flags & FLAG_POUND) != 0 && ofmt == FMTF_STRING) { 1093 flags &= ~FLAG_POUND; 1094 strncpy(visbuf, sdata, prefixlen); 1095 /* Avoid GCC warnings. */ 1096 visbuf[prefixlen] = 0; 1097 strnvis(visbuf + prefixlen, sizeof(visbuf) - prefixlen, 1098 sdata + prefixlen, VIS_WHITE | VIS_OCTAL | VIS_CSTYLE); 1099 sdata = visbuf; 1100 } 1101 1102 /* 1103 * Assemble the format string for passing to printf(3). 1104 */ 1105 lfmt[0] = '\0'; 1106 (void)strcat(lfmt, "%"); 1107 if (flags & FLAG_POUND) 1108 (void)strcat(lfmt, "#"); 1109 if (flags & FLAG_SPACE) 1110 (void)strcat(lfmt, " "); 1111 if (flags & FLAG_PLUS) 1112 (void)strcat(lfmt, "+"); 1113 if (flags & FLAG_MINUS) 1114 (void)strcat(lfmt, "-"); 1115 if (flags & FLAG_ZERO) 1116 (void)strcat(lfmt, "0"); 1117 1118 /* 1119 * Only the timespecs support the FLOAT output format, and that 1120 * requires work that differs from the other formats. 1121 */ 1122 if (ofmt == FMTF_FLOAT) { 1123 /* 1124 * Nothing after the decimal point, so just print seconds. 1125 */ 1126 if (prec == 0) { 1127 if (size != -1) { 1128 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1129 (void)strcat(lfmt, tmp); 1130 } 1131 (void)strcat(lfmt, "lld"); 1132 return (snprintf(buf, blen, lfmt, 1133 (long long)secs)); 1134 } 1135 1136 /* 1137 * Unspecified precision gets all the precision we have: 1138 * 9 digits. 1139 */ 1140 if (prec == -1) 1141 prec = 9; 1142 1143 /* 1144 * Adjust the size for the decimal point and the digits 1145 * that will follow. 1146 */ 1147 size -= prec + 1; 1148 1149 /* 1150 * Any leftover size that's legitimate will be used. 1151 */ 1152 if (size > 0) { 1153 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1154 (void)strcat(lfmt, tmp); 1155 } 1156 /* Seconds: time_t cast to long long. */ 1157 (void)strcat(lfmt, "lld"); 1158 1159 /* 1160 * The stuff after the decimal point always needs zero 1161 * filling. 1162 */ 1163 (void)strcat(lfmt, ".%0"); 1164 1165 /* 1166 * We can "print" at most nine digits of precision. The 1167 * rest we will pad on at the end. 1168 * 1169 * Nanoseconds: long. 1170 */ 1171 (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); 1172 (void)strcat(lfmt, tmp); 1173 1174 /* 1175 * For precision of less that nine digits, trim off the 1176 * less significant figures. 1177 */ 1178 for (; prec < 9; prec++) 1179 nsecs /= 10; 1180 1181 /* 1182 * Use the format, and then tack on any zeroes that 1183 * might be required to make up the requested precision. 1184 */ 1185 l = snprintf(buf, blen, lfmt, (long long)secs, nsecs); 1186 for (; prec > 9 && l < (int)blen; prec--, l++) 1187 (void)strcat(buf, "0"); 1188 return (l); 1189 } 1190 1191 /* 1192 * Add on size and precision, if specified, to the format. 1193 */ 1194 if (size != -1) { 1195 (void)snprintf(tmp, sizeof(tmp), "%d", size); 1196 (void)strcat(lfmt, tmp); 1197 } 1198 if (prec != -1) { 1199 (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 1200 (void)strcat(lfmt, tmp); 1201 } 1202 1203 /* 1204 * String output uses the temporary sdata. 1205 */ 1206 if (ofmt == FMTF_STRING) { 1207 if (sdata == NULL) 1208 errx(1, "%.*s: bad format", (int)flen, fmt); 1209 (void)strcat(lfmt, "s"); 1210 return (snprintf(buf, blen, lfmt, sdata)); 1211 } 1212 1213 /* 1214 * Ensure that sign extension does not cause bad looking output 1215 * for some forms. 1216 */ 1217 if (small && ofmt != FMTF_DECIMAL) 1218 data = (uint32_t)data; 1219 1220 /* 1221 * The four "numeric" output forms. 1222 */ 1223 (void)strcat(lfmt, "ll"); 1224 switch (ofmt) { 1225 case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; 1226 case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; 1227 case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; 1228 case FMTF_HEX: (void)strcat(lfmt, "x"); break; 1229 } 1230 1231 /* 1232 * shift and round to nearest for kilobytes, megabytes, 1233 * gigabytes. 1234 */ 1235 if (shift > 0) { 1236 data >>= (shift - 1); 1237 data++; 1238 data >>= 1; 1239 } 1240 1241 return (snprintf(buf, blen, lfmt, data)); 1242 } 1243