1 /* $OpenBSD: stat.c,v 1.17 2010/10/29 05:36:25 guenther 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 lsF = 0; 162 fmtchar = '\0'; 163 usestat = 0; 164 nonl = 0; 165 quiet = 0; 166 statfmt = NULL; 167 timefmt = NULL; 168 169 options = "f:FlLnqrst:x"; 170 synopsis = "[-FLnq] [-f format | -l | -r | -s | -x] " 171 "[-t timefmt] [file ...]"; 172 173 while ((ch = getopt(argc, argv, options)) != -1) 174 switch (ch) { 175 case 'F': 176 lsF = 1; 177 break; 178 case 'L': 179 usestat = 1; 180 break; 181 case 'n': 182 nonl = 1; 183 break; 184 case 'q': 185 quiet = 1; 186 break; 187 case 'f': 188 statfmt = optarg; 189 /* FALLTHROUGH */ 190 case 'l': 191 case 'r': 192 case 's': 193 case 'x': 194 if (fmtchar != 0) 195 errx(1, "can't use format '%c' with '%c'", 196 fmtchar, ch); 197 fmtchar = ch; 198 break; 199 case 't': 200 timefmt = optarg; 201 break; 202 default: 203 usage(synopsis); 204 } 205 206 argc -= optind; 207 argv += optind; 208 fn = 1; 209 210 if (fmtchar == '\0') { 211 if (lsF) 212 fmtchar = 'l'; 213 else { 214 fmtchar = 'f'; 215 statfmt = DEF_FORMAT; 216 } 217 } 218 219 if (lsF && fmtchar != 'l') 220 errx(1, "can't use format '%c' with -F", fmtchar); 221 222 switch (fmtchar) { 223 case 'f': 224 /* statfmt already set */ 225 break; 226 case 'l': 227 statfmt = lsF ? LSF_FORMAT : LS_FORMAT; 228 break; 229 case 'r': 230 statfmt = RAW_FORMAT; 231 break; 232 case 's': 233 statfmt = SHELL_FORMAT; 234 break; 235 case 'x': 236 statfmt = LINUX_FORMAT; 237 if (timefmt == NULL) 238 timefmt = "%c"; 239 break; 240 default: 241 usage(synopsis); 242 /*NOTREACHED*/ 243 } 244 245 if (timefmt == NULL) 246 timefmt = TIME_FORMAT; 247 248 errs = 0; 249 do { 250 if (argc == 0) 251 rc = fstat(STDIN_FILENO, &st); 252 else if (usestat) { 253 /* 254 * Try stat() and if it fails, fall back to 255 * lstat() just in case we're examining a 256 * broken symlink. 257 */ 258 if ((rc = stat(argv[0], &st)) == -1 && 259 errno == ENOENT && 260 (rc = lstat(argv[0], &st)) == -1) 261 errno = ENOENT; 262 } else 263 rc = lstat(argv[0], &st); 264 265 if (rc == -1) { 266 errs = 1; 267 if (!quiet) 268 warn("%s", 269 argc == 0 ? "(stdin)" : argv[0]); 270 } else 271 output(&st, argv[0], statfmt, fn, nonl); 272 273 argv++; 274 argc--; 275 fn++; 276 } while (argc > 0); 277 278 return (errs); 279 } 280 281 void 282 usage(const char *synopsis) 283 { 284 285 (void)fprintf(stderr, "usage: %s %s\n", __progname, synopsis); 286 exit(1); 287 } 288 289 /* 290 * Parses a format string. 291 */ 292 void 293 output(const struct stat *st, const char *file, 294 const char *statfmt, int fn, int nonl) 295 { 296 int flags, size, prec, ofmt, hilo, what; 297 char buf[PATH_MAX + 4 + 1]; 298 const char *subfmt; 299 int nl, t, i; 300 301 nl = 1; 302 while (*statfmt != '\0') { 303 304 /* 305 * Non-format characters go straight out. 306 */ 307 if (*statfmt != FMT_MAGIC) { 308 addchar(stdout, *statfmt, &nl); 309 statfmt++; 310 continue; 311 } 312 313 /* 314 * The current format "substring" starts here, 315 * and then we skip the magic. 316 */ 317 subfmt = statfmt; 318 statfmt++; 319 320 /* 321 * Some simple one-character "formats". 322 */ 323 switch (*statfmt) { 324 case SIMPLE_NEWLINE: 325 addchar(stdout, '\n', &nl); 326 statfmt++; 327 continue; 328 case SIMPLE_TAB: 329 addchar(stdout, '\t', &nl); 330 statfmt++; 331 continue; 332 case SIMPLE_PERCENT: 333 addchar(stdout, '%', &nl); 334 statfmt++; 335 continue; 336 case SIMPLE_NUMBER: { 337 char num[12], *p; 338 339 snprintf(num, sizeof(num), "%d", fn); 340 for (p = &num[0]; *p; p++) 341 addchar(stdout, *p, &nl); 342 statfmt++; 343 continue; 344 } 345 } 346 347 /* 348 * This must be an actual format string. Format strings are 349 * similar to printf(3) formats up to a point, and are of 350 * the form: 351 * 352 * % required start of format 353 * [-# +0] opt. format characters 354 * size opt. field width 355 * . opt. decimal separator, followed by 356 * prec opt. precision 357 * fmt opt. output specifier (string, numeric, etc.) 358 * sub opt. sub field specifier (high, middle, low) 359 * datum required field specifier (size, mode, etc) 360 * 361 * Only the % and the datum selector are required. All data 362 * have reasonable default output forms. The "sub" specifier 363 * only applies to certain data (mode, dev, rdev, filetype). 364 * The symlink output defaults to STRING, yet will only emit 365 * the leading " -> " if STRING is explicitly specified. The 366 * sizerdev datum will generate rdev output for character or 367 * block devices, and size output for all others. 368 */ 369 flags = 0; 370 do { 371 if (*statfmt == FMT_POUND) 372 flags |= FLAG_POUND; 373 else if (*statfmt == FMT_SPACE) 374 flags |= FLAG_SPACE; 375 else if (*statfmt == FMT_PLUS) 376 flags |= FLAG_PLUS; 377 else if (*statfmt == FMT_ZERO) 378 flags |= FLAG_ZERO; 379 else if (*statfmt == FMT_MINUS) 380 flags |= FLAG_MINUS; 381 else 382 break; 383 statfmt++; 384 } while (1/*CONSTCOND*/); 385 386 size = -1; 387 if (isdigit((unsigned)*statfmt)) { 388 size = 0; 389 while (isdigit((unsigned)*statfmt)) { 390 size = (size * 10) + (*statfmt - '0'); 391 statfmt++; 392 if (size < 0) 393 goto badfmt; 394 } 395 } 396 397 prec = -1; 398 if (*statfmt == FMT_DOT) { 399 statfmt++; 400 401 prec = 0; 402 while (isdigit((unsigned)*statfmt)) { 403 prec = (prec * 10) + (*statfmt - '0'); 404 statfmt++; 405 if (prec < 0) 406 goto badfmt; 407 } 408 } 409 410 #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break 411 #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break 412 switch (*statfmt) { 413 fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); 414 fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); 415 fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); 416 fmtcasef(ofmt, FMT_HEX, FMTF_HEX); 417 fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); 418 fmtcasef(ofmt, FMT_STRING, FMTF_STRING); 419 default: 420 ofmt = 0; 421 break; 422 } 423 424 switch (*statfmt) { 425 fmtcase(hilo, HIGH_PIECE); 426 fmtcase(hilo, MIDDLE_PIECE); 427 fmtcase(hilo, LOW_PIECE); 428 default: 429 hilo = 0; 430 break; 431 } 432 433 switch (*statfmt) { 434 fmtcase(what, SHOW_st_dev); 435 fmtcase(what, SHOW_st_ino); 436 fmtcase(what, SHOW_st_mode); 437 fmtcase(what, SHOW_st_nlink); 438 fmtcase(what, SHOW_st_uid); 439 fmtcase(what, SHOW_st_gid); 440 fmtcase(what, SHOW_st_rdev); 441 fmtcase(what, SHOW_st_atime); 442 fmtcase(what, SHOW_st_mtime); 443 fmtcase(what, SHOW_st_ctime); 444 fmtcase(what, SHOW_st_btime); 445 fmtcase(what, SHOW_st_size); 446 fmtcase(what, SHOW_st_blocks); 447 fmtcase(what, SHOW_st_blksize); 448 fmtcase(what, SHOW_st_flags); 449 fmtcase(what, SHOW_st_gen); 450 fmtcase(what, SHOW_symlink); 451 fmtcase(what, SHOW_filetype); 452 fmtcase(what, SHOW_filename); 453 fmtcase(what, SHOW_sizerdev); 454 default: 455 goto badfmt; 456 } 457 #undef fmtcasef 458 #undef fmtcase 459 460 t = format1(st, file, subfmt, statfmt - subfmt, buf, 461 sizeof(buf), flags, size, prec, ofmt, hilo, what); 462 463 for (i = 0; i < t && i < sizeof(buf) - 1; i++) 464 addchar(stdout, buf[i], &nl); 465 466 continue; 467 468 badfmt: 469 errx(1, "%.*s: bad format", 470 (int)(statfmt - subfmt + 1), subfmt); 471 } 472 473 if (!nl && !nonl) 474 (void)fputc('\n', stdout); 475 (void)fflush(stdout); 476 } 477 478 /* 479 * Arranges output according to a single parsed format substring. 480 */ 481 int 482 format1(const struct stat *st, 483 const char *file, 484 const char *fmt, int flen, 485 char *buf, size_t blen, 486 int flags, int size, int prec, int ofmt, 487 int hilo, int what) 488 { 489 u_int64_t data; 490 char *sdata, lfmt[24], tmp[20]; 491 char smode[12], sid[12], path[PATH_MAX + 4]; 492 struct passwd *pw; 493 struct group *gr; 494 struct tm *tm; 495 time_t secs; 496 long nsecs; 497 int l, small, formats, gottime, n; 498 499 formats = 0; 500 small = 0; 501 gottime = 0; 502 secs = 0; 503 nsecs = 0; 504 505 /* 506 * First, pick out the data and tweak it based on hilo or 507 * specified output format (symlink output only). 508 */ 509 switch (what) { 510 case SHOW_st_dev: 511 case SHOW_st_rdev: 512 small = (sizeof(st->st_dev) == 4); 513 data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; 514 sdata = (what == SHOW_st_dev) ? 515 devname(st->st_dev, S_IFBLK) : 516 devname(st->st_rdev, 517 S_ISCHR(st->st_mode) ? S_IFCHR : 518 S_ISBLK(st->st_mode) ? S_IFBLK : 519 0U); 520 if (sdata == NULL) 521 sdata = "???"; 522 if (hilo == HIGH_PIECE) { 523 data = major(data); 524 hilo = 0; 525 } else if (hilo == LOW_PIECE) { 526 data = minor((unsigned)data); 527 hilo = 0; 528 } 529 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 530 FMTF_STRING; 531 if (ofmt == 0) 532 ofmt = FMTF_UNSIGNED; 533 break; 534 case SHOW_st_ino: 535 small = (sizeof(st->st_ino) == 4); 536 data = st->st_ino; 537 sdata = NULL; 538 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 539 if (ofmt == 0) 540 ofmt = FMTF_UNSIGNED; 541 break; 542 case SHOW_st_mode: 543 small = (sizeof(st->st_mode) == 4); 544 data = st->st_mode; 545 strmode(st->st_mode, smode); 546 sdata = smode; 547 l = strlen(sdata); 548 if (sdata[l - 1] == ' ') 549 sdata[--l] = '\0'; 550 if (hilo == HIGH_PIECE) { 551 data >>= 12; 552 sdata += 1; 553 sdata[3] = '\0'; 554 hilo = 0; 555 } else if (hilo == MIDDLE_PIECE) { 556 data = (data >> 9) & 07; 557 sdata += 4; 558 sdata[3] = '\0'; 559 hilo = 0; 560 } else if (hilo == LOW_PIECE) { 561 data &= 0777; 562 sdata += 7; 563 sdata[3] = '\0'; 564 hilo = 0; 565 } 566 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 567 FMTF_STRING; 568 if (ofmt == 0) 569 ofmt = FMTF_OCTAL; 570 break; 571 case SHOW_st_nlink: 572 small = (sizeof(st->st_dev) == 4); 573 data = st->st_nlink; 574 sdata = NULL; 575 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 576 if (ofmt == 0) 577 ofmt = FMTF_UNSIGNED; 578 break; 579 case SHOW_st_uid: 580 small = (sizeof(st->st_uid) == 4); 581 data = st->st_uid; 582 if ((pw = getpwuid(st->st_uid)) != NULL) 583 sdata = pw->pw_name; 584 else { 585 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); 586 sdata = sid; 587 } 588 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 589 FMTF_STRING; 590 if (ofmt == 0) 591 ofmt = FMTF_UNSIGNED; 592 break; 593 case SHOW_st_gid: 594 small = (sizeof(st->st_gid) == 4); 595 data = st->st_gid; 596 if ((gr = getgrgid(st->st_gid)) != NULL) 597 sdata = gr->gr_name; 598 else { 599 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); 600 sdata = sid; 601 } 602 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 603 FMTF_STRING; 604 if (ofmt == 0) 605 ofmt = FMTF_UNSIGNED; 606 break; 607 case SHOW_st_atime: 608 gottime = 1; 609 secs = st->st_atime; 610 nsecs = st->st_atimensec; 611 /* FALLTHROUGH */ 612 case SHOW_st_mtime: 613 if (!gottime) { 614 gottime = 1; 615 secs = st->st_mtime; 616 nsecs = st->st_mtimensec; 617 } 618 /* FALLTHROUGH */ 619 case SHOW_st_ctime: 620 if (!gottime) { 621 gottime = 1; 622 secs = st->st_ctime; 623 nsecs = st->st_ctimensec; 624 } 625 /* FALLTHROUGH */ 626 case SHOW_st_btime: 627 if (!gottime) { 628 gottime = 1; 629 secs = st->__st_birthtimespec.tv_sec; 630 nsecs = st->__st_birthtimespec.tv_nsec; 631 } 632 small = (sizeof(secs) == 4); 633 data = secs; 634 small = 1; 635 tm = localtime(&secs); 636 (void)strftime(path, sizeof(path), timefmt, tm); 637 sdata = path; 638 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 639 FMTF_FLOAT | FMTF_STRING; 640 if (ofmt == 0) 641 ofmt = FMTF_DECIMAL; 642 break; 643 case SHOW_st_size: 644 small = (sizeof(st->st_size) == 4); 645 data = st->st_size; 646 sdata = NULL; 647 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 648 if (ofmt == 0) 649 ofmt = FMTF_UNSIGNED; 650 break; 651 case SHOW_st_blocks: 652 small = (sizeof(st->st_blocks) == 4); 653 data = st->st_blocks; 654 sdata = NULL; 655 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 656 if (ofmt == 0) 657 ofmt = FMTF_UNSIGNED; 658 break; 659 case SHOW_st_blksize: 660 small = (sizeof(st->st_blksize) == 4); 661 data = st->st_blksize; 662 sdata = NULL; 663 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 664 if (ofmt == 0) 665 ofmt = FMTF_UNSIGNED; 666 break; 667 case SHOW_st_flags: 668 small = (sizeof(st->st_flags) == 4); 669 data = st->st_flags; 670 sdata = NULL; 671 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 672 if (ofmt == 0) 673 ofmt = FMTF_UNSIGNED; 674 break; 675 case SHOW_st_gen: 676 small = (sizeof(st->st_gen) == 4); 677 data = st->st_gen; 678 sdata = NULL; 679 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 680 if (ofmt == 0) 681 ofmt = FMTF_UNSIGNED; 682 break; 683 case SHOW_symlink: 684 small = 0; 685 data = 0; 686 if (S_ISLNK(st->st_mode)) { 687 snprintf(path, sizeof(path), " -> "); 688 l = readlink(file, path + 4, sizeof(path) - 4 - 1); 689 if (l == -1) { 690 l = 0; 691 path[0] = '\0'; 692 } 693 path[l + 4] = '\0'; 694 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 695 } else 696 sdata = ""; 697 698 formats = FMTF_STRING; 699 if (ofmt == 0) 700 ofmt = FMTF_STRING; 701 break; 702 case SHOW_filetype: 703 small = 0; 704 data = 0; 705 sdata = smode; 706 sdata[0] = '\0'; 707 if (hilo == 0 || hilo == LOW_PIECE) { 708 switch (st->st_mode & S_IFMT) { 709 case S_IFIFO: 710 (void)strlcat(sdata, "|", sizeof(smode)); 711 break; 712 case S_IFDIR: 713 (void)strlcat(sdata, "/", sizeof(smode)); 714 break; 715 case S_IFREG: 716 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 717 (void)strlcat(sdata, "*", 718 sizeof(smode)); 719 break; 720 case S_IFLNK: 721 (void)strlcat(sdata, "@", sizeof(smode)); 722 break; 723 case S_IFSOCK: 724 (void)strlcat(sdata, "=", sizeof(smode)); 725 break; 726 } 727 hilo = 0; 728 } else if (hilo == HIGH_PIECE) { 729 switch (st->st_mode & S_IFMT) { 730 case S_IFIFO: sdata = "Fifo File"; break; 731 case S_IFCHR: sdata = "Character Device"; break; 732 case S_IFDIR: sdata = "Directory"; break; 733 case S_IFBLK: sdata = "Block Device"; break; 734 case S_IFREG: sdata = "Regular File"; break; 735 case S_IFLNK: sdata = "Symbolic Link"; break; 736 case S_IFSOCK: sdata = "Socket"; break; 737 default: sdata = "???"; break; 738 } 739 hilo = 0; 740 } 741 formats = FMTF_STRING; 742 if (ofmt == 0) 743 ofmt = FMTF_STRING; 744 break; 745 case SHOW_filename: 746 small = 0; 747 data = 0; 748 if (file == NULL) 749 (void)strlcpy(path, "(stdin)", sizeof(path)); 750 else 751 (void)strlcpy(path, file, sizeof(path)); 752 sdata = path; 753 formats = FMTF_STRING; 754 if (ofmt == 0) 755 ofmt = FMTF_STRING; 756 break; 757 case SHOW_sizerdev: 758 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 759 char majdev[20], mindev[20]; 760 int l1, l2; 761 762 l1 = format1(st, file, fmt, flen, 763 majdev, sizeof(majdev), flags, size, prec, 764 ofmt, HIGH_PIECE, SHOW_st_rdev); 765 l2 = format1(st, file, fmt, flen, 766 mindev, sizeof(mindev), flags, size, prec, 767 ofmt, LOW_PIECE, SHOW_st_rdev); 768 n = snprintf(buf, blen, "%.*s,%.*s", 769 l1, majdev, l2, mindev); 770 return (n >= blen ? blen : n); 771 } else { 772 return (format1(st, file, fmt, flen, buf, blen, 773 flags, size, prec, ofmt, 0, SHOW_st_size)); 774 } 775 /*NOTREACHED*/ 776 default: 777 errx(1, "%.*s: bad format", (int)flen, fmt); 778 } 779 780 /* 781 * If a subdatum was specified but not supported, or an output 782 * format was selected that is not supported, that's an error. 783 */ 784 if (hilo != 0 || (ofmt & formats) == 0) 785 errx(1, "%.*s: bad format", (int)flen, fmt); 786 787 /* 788 * Assemble the format string for passing to printf(3). 789 */ 790 lfmt[0] = '\0'; 791 (void)strlcat(lfmt, "%", sizeof(lfmt)); 792 if (flags & FLAG_POUND) 793 (void)strlcat(lfmt, "#", sizeof(lfmt)); 794 if (flags & FLAG_SPACE) 795 (void)strlcat(lfmt, " ", sizeof(lfmt)); 796 if (flags & FLAG_PLUS) 797 (void)strlcat(lfmt, "+", sizeof(lfmt)); 798 if (flags & FLAG_MINUS) 799 (void)strlcat(lfmt, "-", sizeof(lfmt)); 800 if (flags & FLAG_ZERO) 801 (void)strlcat(lfmt, "0", sizeof(lfmt)); 802 803 /* 804 * Only the timespecs support the FLOAT output format, and that 805 * requires work that differs from the other formats. 806 */ 807 if (ofmt == FMTF_FLOAT) { 808 /* 809 * Nothing after the decimal point, so just print seconds. 810 */ 811 if (prec == 0) { 812 if (size != -1) { 813 (void)snprintf(tmp, sizeof(tmp), "%d", size); 814 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 815 } 816 (void)strlcat(lfmt, "d", sizeof(lfmt)); 817 n = snprintf(buf, blen, lfmt, secs); 818 return (n >= blen ? blen : n); 819 } 820 821 /* 822 * Unspecified precision gets all the precision we have: 823 * 9 digits. 824 */ 825 if (prec == -1) 826 prec = 9; 827 828 /* 829 * Adjust the size for the decimal point and the digits 830 * that will follow. 831 */ 832 size -= prec + 1; 833 834 /* 835 * Any leftover size that's legitimate will be used. 836 */ 837 if (size > 0) { 838 (void)snprintf(tmp, sizeof(tmp), "%d", size); 839 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 840 } 841 (void)strlcat(lfmt, "d", sizeof(lfmt)); 842 843 /* 844 * The stuff after the decimal point always needs zero 845 * filling. 846 */ 847 (void)strlcat(lfmt, ".%0", sizeof(lfmt)); 848 849 /* 850 * We can "print" at most nine digits of precision. The 851 * rest we will pad on at the end. 852 */ 853 (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec); 854 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 855 856 /* 857 * For precision of less that nine digits, trim off the 858 * less significant figures. 859 */ 860 for (; prec < 9; prec++) 861 nsecs /= 10; 862 863 /* 864 * Use the format, and then tack on any zeroes that 865 * might be required to make up the requested precision. 866 */ 867 l = snprintf(buf, blen, lfmt, secs, nsecs); 868 if (l >= blen) 869 return (l); 870 for (; prec > 9 && l < blen; prec--, l++) 871 (void)strlcat(buf, "0", blen); 872 return (l); 873 } 874 875 /* 876 * Add on size and precision, if specified, to the format. 877 */ 878 if (size != -1) { 879 (void)snprintf(tmp, sizeof(tmp), "%d", size); 880 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 881 } 882 if (prec != -1) { 883 (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 884 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 885 } 886 887 /* 888 * String output uses the temporary sdata. 889 */ 890 if (ofmt == FMTF_STRING) { 891 if (sdata == NULL) 892 errx(1, "%.*s: bad format", (int)flen, fmt); 893 (void)strlcat(lfmt, "s", sizeof(lfmt)); 894 n = snprintf(buf, blen, lfmt, sdata); 895 return (n >= blen ? blen : n); 896 } 897 898 /* 899 * Ensure that sign extension does not cause bad looking output 900 * for some forms. 901 */ 902 if (small && ofmt != FMTF_DECIMAL) 903 data = (u_int32_t)data; 904 905 /* 906 * The four "numeric" output forms. 907 */ 908 (void)strlcat(lfmt, "ll", sizeof(lfmt)); 909 switch (ofmt) { 910 case FMTF_DECIMAL: (void)strlcat(lfmt, "d", sizeof(lfmt)); break; 911 case FMTF_OCTAL: (void)strlcat(lfmt, "o", sizeof(lfmt)); break; 912 case FMTF_UNSIGNED: (void)strlcat(lfmt, "u", sizeof(lfmt)); break; 913 case FMTF_HEX: (void)strlcat(lfmt, "x", sizeof(lfmt)); break; 914 } 915 916 n = snprintf(buf, blen, lfmt, data); 917 return (n >= blen ? blen : n); 918 } 919