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