1/* $OpenBSD: stat.c,v 1.25 2023/08/06 19:36:13 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 133void usage(const char *); 134void output(const struct stat *, const char *, 135 const char *, int, int); 136int 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 143char *timefmt; 144 145#define addchar(s, c, nl) \ 146 do { \ 147 (void)fputc((c), (s)); \ 148 (*nl) = ((c) == '\n'); \ 149 } while (0/*CONSTCOND*/) 150 151extern char *__progname; 152 153int 154main(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 284void 285usage(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 */ 295void 296output(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 */ 484int 485format1(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_atim.tv_nsec; 615 /* FALLTHROUGH */ 616 case SHOW_st_mtime: 617 if (!gottime) { 618 gottime = 1; 619 secs = st->st_mtime; 620 nsecs = st->st_mtim.tv_nsec; 621 } 622 /* FALLTHROUGH */ 623 case SHOW_st_ctime: 624 if (!gottime) { 625 gottime = 1; 626 secs = st->st_ctime; 627 nsecs = st->st_ctim.tv_nsec; 628 } 629 /* FALLTHROUGH */ 630 case SHOW_st_btime: 631 if (!gottime) { 632 gottime = 1; 633 secs = st->__st_birthtime; 634 nsecs = st->__st_birthtim.tv_nsec; 635 } 636 small = (sizeof(secs) == 4); 637 data = secs; 638 tm = localtime(&secs); 639 (void)strftime(path, sizeof(path), timefmt, tm); 640 sdata = path; 641 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 642 FMTF_FLOAT | FMTF_STRING; 643 if (ofmt == 0) 644 ofmt = FMTF_DECIMAL; 645 break; 646 case SHOW_st_size: 647 small = (sizeof(st->st_size) == 4); 648 data = st->st_size; 649 sdata = NULL; 650 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 651 if (ofmt == 0) 652 ofmt = FMTF_UNSIGNED; 653 break; 654 case SHOW_st_blocks: 655 small = (sizeof(st->st_blocks) == 4); 656 data = st->st_blocks; 657 sdata = NULL; 658 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 659 if (ofmt == 0) 660 ofmt = FMTF_UNSIGNED; 661 break; 662 case SHOW_st_blksize: 663 small = (sizeof(st->st_blksize) == 4); 664 data = st->st_blksize; 665 sdata = NULL; 666 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 667 if (ofmt == 0) 668 ofmt = FMTF_UNSIGNED; 669 break; 670 case SHOW_st_flags: 671 small = (sizeof(st->st_flags) == 4); 672 data = st->st_flags; 673 sdata = NULL; 674 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 675 if (ofmt == 0) 676 ofmt = FMTF_UNSIGNED; 677 break; 678 case SHOW_st_gen: 679 small = (sizeof(st->st_gen) == 4); 680 data = st->st_gen; 681 sdata = NULL; 682 formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 683 if (ofmt == 0) 684 ofmt = FMTF_UNSIGNED; 685 break; 686 case SHOW_symlink: 687 small = 0; 688 data = 0; 689 if (S_ISLNK(st->st_mode)) { 690 snprintf(path, sizeof(path), " -> "); 691 l = readlink(file, path + 4, sizeof(path) - 4 - 1); 692 if (l == -1) { 693 l = 0; 694 path[0] = '\0'; 695 } 696 path[l + 4] = '\0'; 697 sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 698 } else 699 sdata = ""; 700 701 formats = FMTF_STRING; 702 if (ofmt == 0) 703 ofmt = FMTF_STRING; 704 break; 705 case SHOW_filetype: 706 small = 0; 707 data = 0; 708 sdata = smode; 709 smode[0] = '\0'; 710 if (hilo == 0 || hilo == LOW_PIECE) { 711 switch (st->st_mode & S_IFMT) { 712 case S_IFIFO: 713 (void)strlcat(smode, "|", sizeof(smode)); 714 break; 715 case S_IFDIR: 716 (void)strlcat(smode, "/", sizeof(smode)); 717 break; 718 case S_IFREG: 719 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 720 (void)strlcat(smode, "*", 721 sizeof(smode)); 722 break; 723 case S_IFLNK: 724 (void)strlcat(smode, "@", sizeof(smode)); 725 break; 726 case S_IFSOCK: 727 (void)strlcat(smode, "=", sizeof(smode)); 728 break; 729 } 730 hilo = 0; 731 } else if (hilo == HIGH_PIECE) { 732 switch (st->st_mode & S_IFMT) { 733 case S_IFIFO: sdata = "Fifo File"; break; 734 case S_IFCHR: sdata = "Character Device"; break; 735 case S_IFDIR: sdata = "Directory"; break; 736 case S_IFBLK: sdata = "Block Device"; break; 737 case S_IFREG: sdata = "Regular File"; break; 738 case S_IFLNK: sdata = "Symbolic Link"; break; 739 case S_IFSOCK: sdata = "Socket"; break; 740 default: sdata = "???"; break; 741 } 742 hilo = 0; 743 } 744 formats = FMTF_STRING; 745 if (ofmt == 0) 746 ofmt = FMTF_STRING; 747 break; 748 case SHOW_filename: 749 small = 0; 750 data = 0; 751 if (file == NULL) 752 (void)strlcpy(path, "(stdin)", sizeof(path)); 753 else 754 (void)strlcpy(path, file, sizeof(path)); 755 sdata = path; 756 formats = FMTF_STRING; 757 if (ofmt == 0) 758 ofmt = FMTF_STRING; 759 break; 760 case SHOW_sizerdev: 761 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 762 char majdev[20], mindev[20]; 763 int l1, l2; 764 765 l1 = format1(st, file, fmt, flen, 766 majdev, sizeof(majdev), flags, size, prec, 767 ofmt, HIGH_PIECE, SHOW_st_rdev); 768 l2 = format1(st, file, fmt, flen, 769 mindev, sizeof(mindev), flags, size, prec, 770 ofmt, LOW_PIECE, SHOW_st_rdev); 771 n = snprintf(buf, blen, "%.*s,%.*s", 772 l1, majdev, l2, mindev); 773 return (n >= blen ? blen : n); 774 } else { 775 return (format1(st, file, fmt, flen, buf, blen, 776 flags, size, prec, ofmt, 0, SHOW_st_size)); 777 } 778 /*NOTREACHED*/ 779 default: 780 errx(1, "%.*s: bad format", (int)flen, fmt); 781 } 782 783 /* 784 * If a subdatum was specified but not supported, or an output 785 * format was selected that is not supported, that's an error. 786 */ 787 if (hilo != 0 || (ofmt & formats) == 0) 788 errx(1, "%.*s: bad format", (int)flen, fmt); 789 790 /* 791 * Assemble the format string for passing to printf(3). 792 */ 793 lfmt[0] = '\0'; 794 (void)strlcat(lfmt, "%", sizeof(lfmt)); 795 if (flags & FLAG_POUND) 796 (void)strlcat(lfmt, "#", sizeof(lfmt)); 797 if (flags & FLAG_SPACE) 798 (void)strlcat(lfmt, " ", sizeof(lfmt)); 799 if (flags & FLAG_PLUS) 800 (void)strlcat(lfmt, "+", sizeof(lfmt)); 801 if (flags & FLAG_MINUS) 802 (void)strlcat(lfmt, "-", sizeof(lfmt)); 803 if (flags & FLAG_ZERO) 804 (void)strlcat(lfmt, "0", sizeof(lfmt)); 805 806 /* 807 * Only the timespecs support the FLOAT output format, and that 808 * requires work that differs from the other formats. 809 */ 810 if (ofmt == FMTF_FLOAT) { 811 /* 812 * Nothing after the decimal point, so just print seconds. 813 */ 814 if (prec == 0) { 815 if (size != -1) { 816 (void)snprintf(tmp, sizeof(tmp), "%d", size); 817 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 818 } 819 (void)strlcat(lfmt, "lld", sizeof(lfmt)); 820 n = snprintf(buf, blen, lfmt, (long long)secs); 821 return (n >= blen ? blen : n); 822 } 823 824 /* 825 * Unspecified precision gets all the precision we have: 826 * 9 digits. 827 */ 828 if (prec == -1) 829 prec = 9; 830 831 /* 832 * Adjust the size for the decimal point and the digits 833 * that will follow. 834 */ 835 size -= prec + 1; 836 837 /* 838 * Any leftover size that's legitimate will be used. 839 */ 840 if (size > 0) { 841 (void)snprintf(tmp, sizeof(tmp), "%d", size); 842 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 843 } 844 (void)strlcat(lfmt, "lld", sizeof(lfmt)); 845 846 /* 847 * The stuff after the decimal point always needs zero 848 * filling. 849 */ 850 (void)strlcat(lfmt, ".%0", sizeof(lfmt)); 851 852 /* 853 * We can "print" at most nine digits of precision. The 854 * rest we will pad on at the end. 855 */ 856 (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); 857 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 858 859 /* 860 * For precision of less that nine digits, trim off the 861 * less significant figures. 862 */ 863 for (; prec < 9; prec++) 864 nsecs /= 10; 865 866 /* 867 * Use the format, and then tack on any zeroes that 868 * might be required to make up the requested precision. 869 */ 870 l = snprintf(buf, blen, lfmt, (long long)secs, nsecs); 871 if (l >= blen) 872 return (l); 873 for (; prec > 9 && l < blen; prec--, l++) 874 (void)strlcat(buf, "0", blen); 875 return (l); 876 } 877 878 /* 879 * Add on size and precision, if specified, to the format. 880 */ 881 if (size != -1) { 882 (void)snprintf(tmp, sizeof(tmp), "%d", size); 883 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 884 } 885 if (prec != -1) { 886 (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 887 (void)strlcat(lfmt, tmp, sizeof(lfmt)); 888 } 889 890 /* 891 * String output uses the temporary sdata. 892 */ 893 if (ofmt == FMTF_STRING) { 894 if (sdata == NULL) 895 errx(1, "%.*s: bad format", (int)flen, fmt); 896 (void)strlcat(lfmt, "s", sizeof(lfmt)); 897 n = snprintf(buf, blen, lfmt, sdata); 898 return (n >= blen ? blen : n); 899 } 900 901 /* 902 * Ensure that sign extension does not cause bad looking output 903 * for some forms. 904 */ 905 if (small && ofmt != FMTF_DECIMAL) 906 data = (u_int32_t)data; 907 908 /* 909 * The four "numeric" output forms. 910 */ 911 (void)strlcat(lfmt, "ll", sizeof(lfmt)); 912 switch (ofmt) { 913 case FMTF_DECIMAL: (void)strlcat(lfmt, "d", sizeof(lfmt)); break; 914 case FMTF_OCTAL: (void)strlcat(lfmt, "o", sizeof(lfmt)); break; 915 case FMTF_UNSIGNED: (void)strlcat(lfmt, "u", sizeof(lfmt)); break; 916 case FMTF_HEX: (void)strlcat(lfmt, "x", sizeof(lfmt)); break; 917 } 918 919 n = snprintf(buf, blen, lfmt, data); 920 return (n >= blen ? blen : n); 921} 922