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