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