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