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