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