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