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