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