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