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