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