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