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