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.5 2004/07/09 19:13:40 drhodus 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 int l, small, formats; 507 508 tsp = NULL; 509 formats = 0; 510 small = 0; 511 512 /* 513 * First, pick out the data and tweak it based on hilo or 514 * specified output format (symlink output only). 515 */ 516 switch (what) { 517 case SHOW_st_dev: 518 case SHOW_st_rdev: 519 small = (sizeof(st->st_dev) == 4); 520 data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; 521 sdata = (what == SHOW_st_dev) ? 522 devname(st->st_dev, S_IFBLK) : 523 devname(st->st_rdev, 524 S_ISCHR(st->st_mode) ? S_IFCHR : 525 S_ISBLK(st->st_mode) ? S_IFBLK : 526 0U); 527 if (sdata == NULL) 528 sdata = "???"; 529 if (hilo == HIGH_PIECE) { 530 data = major(data); 531 hilo = 0; 532 } 533 else if (hilo == LOW_PIECE) { 534 data = minor((unsigned)data); 535 hilo = 0; 536 } 537 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 538 FMTF_STRING; 539 if (ofmt == 0) 540 ofmt = FMTF_UNSIGNED; 541 break; 542 case SHOW_st_ino: 543 small = (sizeof(st->st_ino) == 4); 544 data = st->st_ino; 545 sdata = NULL; 546 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 547 if (ofmt == 0) 548 ofmt = FMTF_UNSIGNED; 549 break; 550 case SHOW_st_mode: 551 small = (sizeof(st->st_mode) == 4); 552 data = st->st_mode; 553 strmode(st->st_mode, smode); 554 sdata = smode; 555 l = strlen(sdata); 556 if (sdata[l - 1] == ' ') 557 sdata[--l] = '\0'; 558 if (hilo == HIGH_PIECE) { 559 data >>= 12; 560 sdata += 1; 561 sdata[3] = '\0'; 562 hilo = 0; 563 } 564 else if (hilo == MIDDLE_PIECE) { 565 data = (data >> 9) & 07; 566 sdata += 4; 567 sdata[3] = '\0'; 568 hilo = 0; 569 } 570 else if (hilo == LOW_PIECE) { 571 data &= 0777; 572 sdata += 7; 573 sdata[3] = '\0'; 574 hilo = 0; 575 } 576 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 577 FMTF_STRING; 578 if (ofmt == 0) 579 ofmt = FMTF_OCTAL; 580 break; 581 case SHOW_st_nlink: 582 small = (sizeof(st->st_dev) == 4); 583 data = st->st_nlink; 584 sdata = NULL; 585 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 586 if (ofmt == 0) 587 ofmt = FMTF_UNSIGNED; 588 break; 589 case SHOW_st_uid: 590 small = (sizeof(st->st_uid) == 4); 591 data = st->st_uid; 592 if ((pw = getpwuid(st->st_uid)) != NULL) 593 sdata = pw->pw_name; 594 else { 595 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); 596 sdata = sid; 597 } 598 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 599 FMTF_STRING; 600 if (ofmt == 0) 601 ofmt = FMTF_UNSIGNED; 602 break; 603 case SHOW_st_gid: 604 small = (sizeof(st->st_gid) == 4); 605 data = st->st_gid; 606 if ((gr = getgrgid(st->st_gid)) != NULL) 607 sdata = gr->gr_name; 608 else { 609 snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); 610 sdata = sid; 611 } 612 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 613 FMTF_STRING; 614 if (ofmt == 0) 615 ofmt = FMTF_UNSIGNED; 616 break; 617 case SHOW_st_atime: 618 tsp = &st->st_atimespec; 619 /* FALLTHROUGH */ 620 case SHOW_st_mtime: 621 if (tsp == NULL) 622 tsp = &st->st_mtimespec; 623 /* FALLTHROUGH */ 624 case SHOW_st_ctime: 625 if (tsp == NULL) 626 tsp = &st->st_ctimespec; 627 /* FALLTHROUGH */ 628 case SHOW_st_size: 629 small = (sizeof(st->st_size) == 4); 630 data = st->st_size; 631 sdata = NULL; 632 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 633 if (ofmt == 0) 634 ofmt = FMTF_UNSIGNED; 635 break; 636 case SHOW_st_blocks: 637 small = (sizeof(st->st_blocks) == 4); 638 data = st->st_blocks; 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_blksize: 645 small = (sizeof(st->st_blksize) == 4); 646 data = st->st_blksize; 647 sdata = NULL; 648 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 649 if (ofmt == 0) 650 ofmt = FMTF_UNSIGNED; 651 break; 652 case SHOW_st_flags: 653 small = (sizeof(st->st_flags) == 4); 654 data = st->st_flags; 655 sdata = NULL; 656 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 657 if (ofmt == 0) 658 ofmt = FMTF_UNSIGNED; 659 break; 660 case SHOW_st_gen: 661 small = (sizeof(st->st_gen) == 4); 662 data = st->st_gen; 663 sdata = NULL; 664 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 665 if (ofmt == 0) 666 ofmt = FMTF_UNSIGNED; 667 break; 668 case SHOW_symlink: 669 small = 0; 670 data = 0; 671 if (S_ISLNK(st->st_mode)) { 672 snprintf(path, sizeof(path), " -> "); 673 l = readlink(file, path + 4, sizeof(path) - 4 - 1); 674 if (l == -1) { 675 linkfail = 1; 676 l = 0; 677 path[0] = '\0'; 678 } 679 path[l + 4] = '\0'; 680 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 681 } 682 else { 683 linkfail = 1; 684 sdata = ""; 685 } 686 formats = FMTF_STRING; 687 if (ofmt == 0) 688 ofmt = FMTF_STRING; 689 break; 690 case SHOW_filetype: 691 small = 0; 692 data = 0; 693 sdata = smode; 694 sdata[0] = '\0'; 695 if (hilo == 0 || hilo == LOW_PIECE) { 696 switch (st->st_mode & S_IFMT) { 697 case S_IFIFO: (void)strcat(sdata, "|"); break; 698 case S_IFDIR: (void)strcat(sdata, "/"); break; 699 case S_IFREG: 700 if (st->st_mode & 701 (S_IXUSR | S_IXGRP | S_IXOTH)) 702 (void)strcat(sdata, "*"); 703 break; 704 case S_IFLNK: (void)strcat(sdata, "@"); break; 705 case S_IFSOCK: (void)strcat(sdata, "="); break; 706 case S_IFWHT: (void)strcat(sdata, "%"); break; 707 } 708 hilo = 0; 709 } 710 else if (hilo == HIGH_PIECE) { 711 switch (st->st_mode & S_IFMT) { 712 case S_IFIFO: sdata = "Fifo File"; break; 713 case S_IFCHR: sdata = "Character Device"; break; 714 case S_IFDIR: sdata = "Directory"; break; 715 case S_IFBLK: sdata = "Block Device"; break; 716 case S_IFREG: sdata = "Regular File"; break; 717 case S_IFLNK: sdata = "Symbolic Link"; break; 718 case S_IFSOCK: sdata = "Socket"; break; 719 case S_IFWHT: sdata = "Whiteout File"; break; 720 default: sdata = "???"; break; 721 } 722 hilo = 0; 723 } 724 formats = FMTF_STRING; 725 if (ofmt == 0) 726 ofmt = FMTF_STRING; 727 break; 728 case SHOW_filename: 729 small = 0; 730 data = 0; 731 if (file == NULL) 732 (void)strncpy(path, "(stdin)", sizeof(path)); 733 else 734 (void)strncpy(path, file, sizeof(path)); 735 sdata = path; 736 formats = FMTF_STRING; 737 if (ofmt == 0) 738 ofmt = FMTF_STRING; 739 break; 740 case SHOW_sizerdev: 741 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 742 char majdev[20], mindev[20]; 743 int l1, l2; 744 745 l1 = format1(st, 746 file, 747 fmt, flen, 748 majdev, sizeof(majdev), 749 flags, size, prec, 750 ofmt, HIGH_PIECE, SHOW_st_rdev); 751 l2 = format1(st, 752 file, 753 fmt, flen, 754 mindev, sizeof(mindev), 755 flags, size, prec, 756 ofmt, LOW_PIECE, SHOW_st_rdev); 757 return (snprintf(buf, blen, "%.*s,%.*s", 758 l1, majdev, l2, mindev)); 759 } 760 else { 761 return (format1(st, 762 file, 763 fmt, flen, 764 buf, blen, 765 flags, size, prec, 766 ofmt, 0, SHOW_st_size)); 767 } 768 /*NOTREACHED*/ 769 default: 770 errx(1, "%.*s: bad format", (int)flen, fmt); 771 } 772 773 /* 774 * If a subdatum was specified but not supported, or an output 775 * format was selected that is not supported, that's an error. 776 */ 777 if (hilo != 0 || (ofmt & formats) == 0) 778 errx(1, "%.*s: bad format", (int)flen, fmt); 779 780 /* 781 * Assemble the format string for passing to printf(3). 782 */ 783 lfmt[0] = '\0'; 784 (void)strcat(lfmt, "%"); 785 if (flags & FLAG_POUND) 786 (void)strcat(lfmt, "#"); 787 if (flags & FLAG_SPACE) 788 (void)strcat(lfmt, " "); 789 if (flags & FLAG_PLUS) 790 (void)strcat(lfmt, "+"); 791 if (flags & FLAG_MINUS) 792 (void)strcat(lfmt, "-"); 793 if (flags & FLAG_ZERO) 794 (void)strcat(lfmt, "0"); 795 796 /* 797 * Only the timespecs support the FLOAT output format, and that 798 * requires work that differs from the other formats. 799 */ 800 if (ofmt == FMTF_FLOAT) { 801 /* 802 * Nothing after the decimal point, so just print seconds. 803 */ 804 if (prec == 0) { 805 if (size != -1) { 806 (void)snprintf(tmp, sizeof(tmp), "%d", size); 807 (void)strcat(lfmt, tmp); 808 } 809 (void)strcat(lfmt, "d"); 810 return (snprintf(buf, blen, lfmt, ts.tv_sec)); 811 } 812 813 /* 814 * Unspecified precision gets all the precision we have: 815 * 9 digits. 816 */ 817 if (prec == -1) 818 prec = 9; 819 820 /* 821 * Adjust the size for the decimal point and the digits 822 * that will follow. 823 */ 824 size -= prec + 1; 825 826 /* 827 * Any leftover size that's legitimate will be used. 828 */ 829 if (size > 0) { 830 (void)snprintf(tmp, sizeof(tmp), "%d", size); 831 (void)strcat(lfmt, tmp); 832 } 833 (void)strcat(lfmt, "d"); 834 835 /* 836 * The stuff after the decimal point always needs zero 837 * filling. 838 */ 839 (void)strcat(lfmt, ".%0"); 840 841 /* 842 * We can "print" at most nine digits of precision. The 843 * rest we will pad on at the end. 844 */ 845 (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec); 846 (void)strcat(lfmt, tmp); 847 848 /* 849 * For precision of less that nine digits, trim off the 850 * less significant figures. 851 */ 852 for (; prec < 9; prec++) 853 ts.tv_nsec /= 10; 854 855 /* 856 * Use the format, and then tack on any zeroes that 857 * might be required to make up the requested precision. 858 */ 859 l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec); 860 for (; prec > 9 && l < blen; prec--, l++) 861 (void)strcat(buf, "0"); 862 return (l); 863 } 864 865 /* 866 * Add on size and precision, if specified, to the format. 867 */ 868 if (size != -1) { 869 (void)snprintf(tmp, sizeof(tmp), "%d", size); 870 (void)strcat(lfmt, tmp); 871 } 872 if (prec != -1) { 873 (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 874 (void)strcat(lfmt, tmp); 875 } 876 877 /* 878 * String output uses the temporary sdata. 879 */ 880 if (ofmt == FMTF_STRING) { 881 if (sdata == NULL) 882 errx(1, "%.*s: bad format", (int)flen, fmt); 883 (void)strcat(lfmt, "s"); 884 return (snprintf(buf, blen, lfmt, sdata)); 885 } 886 887 /* 888 * Ensure that sign extension does not cause bad looking output 889 * for some forms. 890 */ 891 if (small && ofmt != FMTF_DECIMAL) 892 data = (u_int32_t)data; 893 894 /* 895 * The four "numeric" output forms. 896 */ 897 (void)strcat(lfmt, "ll"); 898 switch (ofmt) { 899 case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; 900 case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; 901 case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; 902 case FMTF_HEX: (void)strcat(lfmt, "x"); break; 903 } 904 905 return (snprintf(buf, blen, lfmt, data)); 906 } 907