1 1.55 nia /* $NetBSD: stat.c,v 1.55 2025/05/15 19:11:44 nia Exp $ */ 2 1.1 atatat 3 1.1 atatat /* 4 1.36 apb * Copyright (c) 2002-2011 The NetBSD Foundation, Inc. 5 1.1 atatat * All rights reserved. 6 1.1 atatat * 7 1.1 atatat * This code is derived from software contributed to The NetBSD Foundation 8 1.1 atatat * by Andrew Brown. 9 1.1 atatat * 10 1.1 atatat * Redistribution and use in source and binary forms, with or without 11 1.1 atatat * modification, are permitted provided that the following conditions 12 1.1 atatat * are met: 13 1.1 atatat * 1. Redistributions of source code must retain the above copyright 14 1.1 atatat * notice, this list of conditions and the following disclaimer. 15 1.1 atatat * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 atatat * notice, this list of conditions and the following disclaimer in the 17 1.1 atatat * documentation and/or other materials provided with the distribution. 18 1.1 atatat * 19 1.1 atatat * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 atatat * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 atatat * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 atatat * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 atatat * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 atatat * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 atatat * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 atatat * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 atatat * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 atatat * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 atatat * POSSIBILITY OF SUCH DAMAGE. 30 1.1 atatat */ 31 1.1 atatat 32 1.15 lukem #if HAVE_NBTOOL_CONFIG_H 33 1.15 lukem #include "nbtool_config.h" 34 1.38 dsl /* config checked libc, we need the prototype as well */ 35 1.38 dsl #undef HAVE_DEVNAME 36 1.15 lukem #endif 37 1.15 lukem 38 1.1 atatat #include <sys/cdefs.h> 39 1.15 lukem #if !defined(lint) 40 1.55 nia __RCSID("$NetBSD: stat.c,v 1.55 2025/05/15 19:11:44 nia Exp $"); 41 1.11 lukem #endif 42 1.11 lukem 43 1.15 lukem #if ! HAVE_NBTOOL_CONFIG_H 44 1.11 lukem #define HAVE_STRUCT_STAT_ST_FLAGS 1 45 1.13 atatat #define HAVE_STRUCT_STAT_ST_GEN 1 46 1.13 atatat #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 47 1.21 jmc #define HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC 1 48 1.13 atatat #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 49 1.13 atatat #define HAVE_DEVNAME 1 50 1.15 lukem #endif /* HAVE_NBTOOL_CONFIG_H */ 51 1.1 atatat 52 1.10 atatat #include <sys/types.h> 53 1.1 atatat #include <sys/stat.h> 54 1.6 atatat 55 1.6 atatat #include <ctype.h> 56 1.7 atatat #include <err.h> 57 1.18 atatat #include <errno.h> 58 1.6 atatat #include <grp.h> 59 1.8 atatat #include <limits.h> 60 1.6 atatat #include <pwd.h> 61 1.6 atatat #include <stdio.h> 62 1.7 atatat #include <stdlib.h> 63 1.1 atatat #include <string.h> 64 1.6 atatat #include <time.h> 65 1.6 atatat #include <unistd.h> 66 1.50 christos #if HAVE_STRUCT_STAT_ST_FLAGS && !HAVE_NBTOOL_CONFIG_H 67 1.49 christos #include <util.h> 68 1.49 christos #endif 69 1.36 apb #include <vis.h> 70 1.1 atatat 71 1.13 atatat #if HAVE_STRUCT_STAT_ST_FLAGS 72 1.13 atatat #define DEF_F "%#Xf " 73 1.13 atatat #define RAW_F "%f " 74 1.13 atatat #define SHELL_F " st_flags=%f" 75 1.13 atatat #else /* HAVE_STRUCT_STAT_ST_FLAGS */ 76 1.13 atatat #define DEF_F 77 1.13 atatat #define RAW_F 78 1.13 atatat #define SHELL_F 79 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_FLAGS */ 80 1.13 atatat 81 1.13 atatat #if HAVE_STRUCT_STAT_ST_BIRTHTIME 82 1.13 atatat #define DEF_B "\"%SB\" " 83 1.13 atatat #define RAW_B "%B " 84 1.37 erh #define SHELL_B "st_birthtime=%SB " 85 1.40 christos #define LINUX_B "%n Birth: %SB" 86 1.13 atatat #else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 87 1.13 atatat #define DEF_B 88 1.13 atatat #define RAW_B 89 1.13 atatat #define SHELL_B 90 1.40 christos #define LINUX_B 91 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 92 1.13 atatat 93 1.13 atatat #if HAVE_STRUCT_STAT_ST_ATIM 94 1.13 atatat #define st_atimespec st_atim 95 1.13 atatat #define st_ctimespec st_ctim 96 1.13 atatat #define st_mtimespec st_mtim 97 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_ATIM */ 98 1.13 atatat 99 1.1 atatat #define DEF_FORMAT \ 100 1.13 atatat "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ 101 1.13 atatat "%k %b " DEF_F "%N" 102 1.13 atatat #define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ 103 1.13 atatat "%k %b " RAW_F "%N" 104 1.1 atatat #define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" 105 1.1 atatat #define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" 106 1.1 atatat #define SHELL_FORMAT \ 107 1.1 atatat "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ 108 1.1 atatat "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ 109 1.37 erh "st_atime=%Sa st_mtime=%Sm st_ctime=%Sc " SHELL_B \ 110 1.13 atatat "st_blksize=%k st_blocks=%b" SHELL_F 111 1.1 atatat #define LINUX_FORMAT \ 112 1.1 atatat " File: \"%N\"%n" \ 113 1.39 christos " Size: %-11z Blocks: %-11b IO Block: %-11k %HT%n" \ 114 1.39 christos "Device: %Hd,%Ld Inode: %i Links: %l%n" \ 115 1.40 christos " Mode: (%Mp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ 116 1.1 atatat "Access: %Sa%n" \ 117 1.1 atatat "Modify: %Sm%n" \ 118 1.40 christos "Change: %Sc" \ 119 1.40 christos LINUX_B 120 1.1 atatat 121 1.1 atatat #define TIME_FORMAT "%b %e %T %Y" 122 1.1 atatat 123 1.2 atatat #define FLAG_POUND 0x01 124 1.2 atatat #define FLAG_SPACE 0x02 125 1.2 atatat #define FLAG_PLUS 0x04 126 1.2 atatat #define FLAG_ZERO 0x08 127 1.2 atatat #define FLAG_MINUS 0x10 128 1.1 atatat 129 1.1 atatat /* 130 1.2 atatat * These format characters must all be unique, except the magic one. 131 1.1 atatat */ 132 1.2 atatat #define FMT_MAGIC '%' 133 1.2 atatat #define FMT_DOT '.' 134 1.2 atatat 135 1.1 atatat #define SIMPLE_NEWLINE 'n' 136 1.1 atatat #define SIMPLE_TAB 't' 137 1.1 atatat #define SIMPLE_PERCENT '%' 138 1.2 atatat #define SIMPLE_NUMBER '@' 139 1.2 atatat 140 1.2 atatat #define FMT_POUND '#' 141 1.2 atatat #define FMT_SPACE ' ' 142 1.2 atatat #define FMT_PLUS '+' 143 1.2 atatat #define FMT_ZERO '0' 144 1.2 atatat #define FMT_MINUS '-' 145 1.1 atatat 146 1.1 atatat #define FMT_DECIMAL 'D' 147 1.1 atatat #define FMT_OCTAL 'O' 148 1.1 atatat #define FMT_UNSIGNED 'U' 149 1.1 atatat #define FMT_HEX 'X' 150 1.1 atatat #define FMT_FLOAT 'F' 151 1.1 atatat #define FMT_STRING 'S' 152 1.1 atatat 153 1.5 atatat #define FMTF_DECIMAL 0x01 154 1.5 atatat #define FMTF_OCTAL 0x02 155 1.5 atatat #define FMTF_UNSIGNED 0x04 156 1.5 atatat #define FMTF_HEX 0x08 157 1.5 atatat #define FMTF_FLOAT 0x10 158 1.5 atatat #define FMTF_STRING 0x20 159 1.5 atatat 160 1.1 atatat #define HIGH_PIECE 'H' 161 1.1 atatat #define MIDDLE_PIECE 'M' 162 1.1 atatat #define LOW_PIECE 'L' 163 1.1 atatat 164 1.24 elad #define SHOW_realpath 'R' 165 1.1 atatat #define SHOW_st_dev 'd' 166 1.1 atatat #define SHOW_st_ino 'i' 167 1.1 atatat #define SHOW_st_mode 'p' 168 1.1 atatat #define SHOW_st_nlink 'l' 169 1.1 atatat #define SHOW_st_uid 'u' 170 1.1 atatat #define SHOW_st_gid 'g' 171 1.1 atatat #define SHOW_st_rdev 'r' 172 1.1 atatat #define SHOW_st_atime 'a' 173 1.1 atatat #define SHOW_st_mtime 'm' 174 1.1 atatat #define SHOW_st_ctime 'c' 175 1.10 atatat #define SHOW_st_btime 'B' 176 1.1 atatat #define SHOW_st_size 'z' 177 1.1 atatat #define SHOW_st_blocks 'b' 178 1.1 atatat #define SHOW_st_blksize 'k' 179 1.1 atatat #define SHOW_st_flags 'f' 180 1.1 atatat #define SHOW_st_gen 'v' 181 1.1 atatat #define SHOW_symlink 'Y' 182 1.1 atatat #define SHOW_filetype 'T' 183 1.1 atatat #define SHOW_filename 'N' 184 1.1 atatat #define SHOW_sizerdev 'Z' 185 1.1 atatat 186 1.35 joerg static void usage(const char *) __dead; 187 1.35 joerg static void output(const struct stat *, const char *, 188 1.34 christos const char *, int, int, int); 189 1.35 joerg static int format1(const struct stat *, /* stat info */ 190 1.1 atatat const char *, /* the file name */ 191 1.1 atatat const char *, int, /* the format string itself */ 192 1.1 atatat char *, size_t, /* a place to put the output */ 193 1.1 atatat int, int, int, int, /* the parsed format */ 194 1.34 christos int, int, int); 195 1.1 atatat 196 1.35 joerg static const char *timefmt; 197 1.35 joerg static int linkfail; 198 1.1 atatat 199 1.4 atatat #define addchar(s, c, nl) \ 200 1.1 atatat do { \ 201 1.4 atatat (void)fputc((c), (s)); \ 202 1.4 atatat (*nl) = ((c) == '\n'); \ 203 1.1 atatat } while (0/*CONSTCOND*/) 204 1.1 atatat 205 1.1 atatat int 206 1.1 atatat main(int argc, char *argv[]) 207 1.1 atatat { 208 1.1 atatat struct stat st; 209 1.4 atatat int ch, rc, errs, am_readlink; 210 1.4 atatat int lsF, fmtchar, usestat, fn, nonl, quiet; 211 1.28 lukem const char *statfmt, *options, *synopsis; 212 1.1 atatat 213 1.4 atatat am_readlink = 0; 214 1.1 atatat lsF = 0; 215 1.1 atatat fmtchar = '\0'; 216 1.1 atatat usestat = 0; 217 1.1 atatat nonl = 0; 218 1.4 atatat quiet = 0; 219 1.4 atatat linkfail = 0; 220 1.1 atatat statfmt = NULL; 221 1.1 atatat timefmt = NULL; 222 1.1 atatat 223 1.34 christos setprogname(argv[0]); 224 1.34 christos 225 1.4 atatat if (strcmp(getprogname(), "readlink") == 0) { 226 1.4 atatat am_readlink = 1; 227 1.34 christos options = "fnqsv"; 228 1.54 kre synopsis = "[-fnqsv] file ..."; 229 1.4 atatat statfmt = "%Y"; 230 1.4 atatat fmtchar = 'f'; 231 1.54 kre if (getenv("POSIXLY_CORRECT") == NULL) 232 1.54 kre quiet = 1; 233 1.4 atatat } else { 234 1.4 atatat options = "f:FlLnqrst:x"; 235 1.4 atatat synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; 236 1.4 atatat } 237 1.4 atatat 238 1.4 atatat while ((ch = getopt(argc, argv, options)) != -1) 239 1.1 atatat switch (ch) { 240 1.1 atatat case 'F': 241 1.1 atatat lsF = 1; 242 1.1 atatat break; 243 1.1 atatat case 'L': 244 1.1 atatat usestat = 1; 245 1.1 atatat break; 246 1.1 atatat case 'n': 247 1.1 atatat nonl = 1; 248 1.1 atatat break; 249 1.4 atatat case 'q': 250 1.4 atatat quiet = 1; 251 1.4 atatat break; 252 1.1 atatat case 'f': 253 1.24 elad if (am_readlink) { 254 1.24 elad statfmt = "%R"; 255 1.24 elad break; 256 1.24 elad } 257 1.1 atatat statfmt = optarg; 258 1.1 atatat /* FALLTHROUGH */ 259 1.1 atatat case 'l': 260 1.1 atatat case 'r': 261 1.1 atatat case 's': 262 1.34 christos if (am_readlink) { 263 1.34 christos quiet = 1; 264 1.34 christos break; 265 1.34 christos } 266 1.34 christos /*FALLTHROUGH*/ 267 1.1 atatat case 'x': 268 1.1 atatat if (fmtchar != 0) 269 1.1 atatat errx(1, "can't use format '%c' with '%c'", 270 1.1 atatat fmtchar, ch); 271 1.1 atatat fmtchar = ch; 272 1.1 atatat break; 273 1.1 atatat case 't': 274 1.1 atatat timefmt = optarg; 275 1.1 atatat break; 276 1.34 christos case 'v': 277 1.34 christos quiet = 0; 278 1.34 christos break; 279 1.1 atatat default: 280 1.4 atatat usage(synopsis); 281 1.1 atatat } 282 1.1 atatat 283 1.1 atatat argc -= optind; 284 1.1 atatat argv += optind; 285 1.2 atatat fn = 1; 286 1.1 atatat 287 1.54 kre if (am_readlink && argc == 0) 288 1.54 kre usage(synopsis); 289 1.54 kre 290 1.1 atatat if (fmtchar == '\0') { 291 1.1 atatat if (lsF) 292 1.1 atatat fmtchar = 'l'; 293 1.1 atatat else { 294 1.1 atatat fmtchar = 'f'; 295 1.1 atatat statfmt = DEF_FORMAT; 296 1.1 atatat } 297 1.1 atatat } 298 1.1 atatat 299 1.1 atatat if (lsF && fmtchar != 'l') 300 1.1 atatat errx(1, "can't use format '%c' with -F", fmtchar); 301 1.1 atatat 302 1.1 atatat switch (fmtchar) { 303 1.1 atatat case 'f': 304 1.1 atatat /* statfmt already set */ 305 1.1 atatat break; 306 1.1 atatat case 'l': 307 1.1 atatat statfmt = lsF ? LSF_FORMAT : LS_FORMAT; 308 1.1 atatat break; 309 1.1 atatat case 'r': 310 1.1 atatat statfmt = RAW_FORMAT; 311 1.1 atatat break; 312 1.1 atatat case 's': 313 1.1 atatat statfmt = SHELL_FORMAT; 314 1.37 erh if (timefmt == NULL) 315 1.37 erh timefmt = "%s"; 316 1.1 atatat break; 317 1.1 atatat case 'x': 318 1.1 atatat statfmt = LINUX_FORMAT; 319 1.1 atatat if (timefmt == NULL) 320 1.41 kre timefmt = "%Y-%m-%d %H:%M:%S.%f %z"; 321 1.1 atatat break; 322 1.1 atatat default: 323 1.4 atatat usage(synopsis); 324 1.1 atatat /*NOTREACHED*/ 325 1.1 atatat } 326 1.1 atatat 327 1.1 atatat if (timefmt == NULL) 328 1.1 atatat timefmt = TIME_FORMAT; 329 1.1 atatat 330 1.1 atatat errs = 0; 331 1.1 atatat do { 332 1.48 kre if (argc == 0) { 333 1.48 kre fn = 0; 334 1.1 atatat rc = fstat(STDIN_FILENO, &st); 335 1.48 kre } else if (usestat) { 336 1.18 atatat /* 337 1.18 atatat * Try stat() and if it fails, fall back to 338 1.18 atatat * lstat() just in case we're examining a 339 1.18 atatat * broken symlink. 340 1.18 atatat */ 341 1.18 atatat if ((rc = stat(argv[0], &st)) == -1 && 342 1.18 atatat errno == ENOENT && 343 1.18 atatat (rc = lstat(argv[0], &st)) == -1) 344 1.18 atatat errno = ENOENT; 345 1.48 kre } else 346 1.1 atatat rc = lstat(argv[0], &st); 347 1.1 atatat 348 1.1 atatat if (rc == -1) { 349 1.1 atatat errs = 1; 350 1.4 atatat linkfail = 1; 351 1.54 kre if (!quiet) { 352 1.54 kre if (argc == 0) 353 1.54 kre warn("%s: %s", 354 1.54 kre "(stdin)", "fstat"); 355 1.54 kre else 356 1.55 nia warn("%s: %s", argv[0], 357 1.55 nia usestat ? "stat" : "lstat"); 358 1.54 kre } 359 1.54 kre } else if (am_readlink && statfmt[1] == 'Y' && 360 1.54 kre (st.st_mode & S_IFMT) != S_IFLNK) { 361 1.54 kre linkfail = 1; 362 1.4 atatat if (!quiet) 363 1.54 kre warnx("%s: Not a symbolic link", argv[0]); 364 1.48 kre } else 365 1.34 christos output(&st, argv[0], statfmt, fn, nonl, quiet); 366 1.1 atatat 367 1.1 atatat argv++; 368 1.1 atatat argc--; 369 1.2 atatat fn++; 370 1.1 atatat } while (argc > 0); 371 1.1 atatat 372 1.4 atatat return (am_readlink ? linkfail : errs); 373 1.1 atatat } 374 1.1 atatat 375 1.35 joerg static void 376 1.4 atatat usage(const char *synopsis) 377 1.1 atatat { 378 1.1 atatat 379 1.4 atatat (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); 380 1.1 atatat exit(1); 381 1.1 atatat } 382 1.1 atatat 383 1.1 atatat /* 384 1.1 atatat * Parses a format string. 385 1.1 atatat */ 386 1.35 joerg static void 387 1.1 atatat output(const struct stat *st, const char *file, 388 1.34 christos const char *statfmt, int fn, int nonl, int quiet) 389 1.1 atatat { 390 1.1 atatat int flags, size, prec, ofmt, hilo, what; 391 1.36 apb /* 392 1.36 apb * buf size is enough for an item of length PATH_MAX, 393 1.36 apb * multiplied by 4 for vis encoding, plus 4 for symlink 394 1.36 apb * " -> " prefix, plus 1 for \0 terminator. 395 1.36 apb */ 396 1.36 apb char buf[PATH_MAX * 4 + 4 + 1]; 397 1.1 atatat const char *subfmt; 398 1.1 atatat int nl, t, i; 399 1.1 atatat 400 1.4 atatat nl = 1; 401 1.1 atatat while (*statfmt != '\0') { 402 1.1 atatat 403 1.1 atatat /* 404 1.1 atatat * Non-format characters go straight out. 405 1.1 atatat */ 406 1.2 atatat if (*statfmt != FMT_MAGIC) { 407 1.4 atatat addchar(stdout, *statfmt, &nl); 408 1.1 atatat statfmt++; 409 1.1 atatat continue; 410 1.1 atatat } 411 1.1 atatat 412 1.1 atatat /* 413 1.1 atatat * The current format "substring" starts here, 414 1.2 atatat * and then we skip the magic. 415 1.1 atatat */ 416 1.1 atatat subfmt = statfmt; 417 1.1 atatat statfmt++; 418 1.1 atatat 419 1.1 atatat /* 420 1.1 atatat * Some simple one-character "formats". 421 1.1 atatat */ 422 1.1 atatat switch (*statfmt) { 423 1.1 atatat case SIMPLE_NEWLINE: 424 1.4 atatat addchar(stdout, '\n', &nl); 425 1.1 atatat statfmt++; 426 1.1 atatat continue; 427 1.1 atatat case SIMPLE_TAB: 428 1.4 atatat addchar(stdout, '\t', &nl); 429 1.1 atatat statfmt++; 430 1.1 atatat continue; 431 1.1 atatat case SIMPLE_PERCENT: 432 1.4 atatat addchar(stdout, '%', &nl); 433 1.1 atatat statfmt++; 434 1.1 atatat continue; 435 1.2 atatat case SIMPLE_NUMBER: { 436 1.2 atatat char num[12], *p; 437 1.2 atatat 438 1.2 atatat snprintf(num, sizeof(num), "%d", fn); 439 1.2 atatat for (p = &num[0]; *p; p++) 440 1.4 atatat addchar(stdout, *p, &nl); 441 1.2 atatat statfmt++; 442 1.2 atatat continue; 443 1.2 atatat } 444 1.1 atatat } 445 1.1 atatat 446 1.1 atatat /* 447 1.1 atatat * This must be an actual format string. Format strings are 448 1.1 atatat * similar to printf(3) formats up to a point, and are of 449 1.1 atatat * the form: 450 1.1 atatat * 451 1.1 atatat * % required start of format 452 1.1 atatat * [-# +0] opt. format characters 453 1.1 atatat * size opt. field width 454 1.1 atatat * . opt. decimal separator, followed by 455 1.1 atatat * prec opt. precision 456 1.1 atatat * fmt opt. output specifier (string, numeric, etc.) 457 1.1 atatat * sub opt. sub field specifier (high, middle, low) 458 1.1 atatat * datum required field specifier (size, mode, etc) 459 1.1 atatat * 460 1.1 atatat * Only the % and the datum selector are required. All data 461 1.1 atatat * have reasonable default output forms. The "sub" specifier 462 1.1 atatat * only applies to certain data (mode, dev, rdev, filetype). 463 1.1 atatat * The symlink output defaults to STRING, yet will only emit 464 1.1 atatat * the leading " -> " if STRING is explicitly specified. The 465 1.1 atatat * sizerdev datum will generate rdev output for character or 466 1.1 atatat * block devices, and size output for all others. 467 1.36 apb * For STRING output, the # format requests vis encoding. 468 1.1 atatat */ 469 1.1 atatat flags = 0; 470 1.1 atatat do { 471 1.2 atatat if (*statfmt == FMT_POUND) 472 1.2 atatat flags |= FLAG_POUND; 473 1.2 atatat else if (*statfmt == FMT_SPACE) 474 1.2 atatat flags |= FLAG_SPACE; 475 1.2 atatat else if (*statfmt == FMT_PLUS) 476 1.2 atatat flags |= FLAG_PLUS; 477 1.2 atatat else if (*statfmt == FMT_ZERO) 478 1.2 atatat flags |= FLAG_ZERO; 479 1.2 atatat else if (*statfmt == FMT_MINUS) 480 1.2 atatat flags |= FLAG_MINUS; 481 1.1 atatat else 482 1.1 atatat break; 483 1.1 atatat statfmt++; 484 1.1 atatat } while (1/*CONSTCOND*/); 485 1.1 atatat 486 1.1 atatat size = -1; 487 1.47 rillig if (isdigit((unsigned char)*statfmt)) { 488 1.1 atatat size = 0; 489 1.47 rillig while (isdigit((unsigned char)*statfmt)) { 490 1.1 atatat size = (size * 10) + (*statfmt - '0'); 491 1.1 atatat statfmt++; 492 1.1 atatat if (size < 0) 493 1.1 atatat goto badfmt; 494 1.1 atatat } 495 1.1 atatat } 496 1.1 atatat 497 1.1 atatat prec = -1; 498 1.2 atatat if (*statfmt == FMT_DOT) { 499 1.1 atatat statfmt++; 500 1.1 atatat 501 1.1 atatat prec = 0; 502 1.47 rillig while (isdigit((unsigned char)*statfmt)) { 503 1.1 atatat prec = (prec * 10) + (*statfmt - '0'); 504 1.1 atatat statfmt++; 505 1.1 atatat if (prec < 0) 506 1.1 atatat goto badfmt; 507 1.1 atatat } 508 1.1 atatat } 509 1.1 atatat 510 1.5 atatat #define fmtcase(x, y) case (y): (x) = (y); statfmt++; break 511 1.5 atatat #define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break 512 1.1 atatat switch (*statfmt) { 513 1.5 atatat fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); 514 1.5 atatat fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); 515 1.5 atatat fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); 516 1.5 atatat fmtcasef(ofmt, FMT_HEX, FMTF_HEX); 517 1.5 atatat fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); 518 1.5 atatat fmtcasef(ofmt, FMT_STRING, FMTF_STRING); 519 1.1 atatat default: 520 1.1 atatat ofmt = 0; 521 1.1 atatat break; 522 1.1 atatat } 523 1.1 atatat 524 1.1 atatat switch (*statfmt) { 525 1.1 atatat fmtcase(hilo, HIGH_PIECE); 526 1.1 atatat fmtcase(hilo, MIDDLE_PIECE); 527 1.1 atatat fmtcase(hilo, LOW_PIECE); 528 1.1 atatat default: 529 1.1 atatat hilo = 0; 530 1.1 atatat break; 531 1.1 atatat } 532 1.1 atatat 533 1.1 atatat switch (*statfmt) { 534 1.24 elad fmtcase(what, SHOW_realpath); 535 1.1 atatat fmtcase(what, SHOW_st_dev); 536 1.1 atatat fmtcase(what, SHOW_st_ino); 537 1.1 atatat fmtcase(what, SHOW_st_mode); 538 1.1 atatat fmtcase(what, SHOW_st_nlink); 539 1.1 atatat fmtcase(what, SHOW_st_uid); 540 1.1 atatat fmtcase(what, SHOW_st_gid); 541 1.1 atatat fmtcase(what, SHOW_st_rdev); 542 1.1 atatat fmtcase(what, SHOW_st_atime); 543 1.1 atatat fmtcase(what, SHOW_st_mtime); 544 1.1 atatat fmtcase(what, SHOW_st_ctime); 545 1.10 atatat fmtcase(what, SHOW_st_btime); 546 1.1 atatat fmtcase(what, SHOW_st_size); 547 1.1 atatat fmtcase(what, SHOW_st_blocks); 548 1.1 atatat fmtcase(what, SHOW_st_blksize); 549 1.1 atatat fmtcase(what, SHOW_st_flags); 550 1.1 atatat fmtcase(what, SHOW_st_gen); 551 1.1 atatat fmtcase(what, SHOW_symlink); 552 1.1 atatat fmtcase(what, SHOW_filetype); 553 1.1 atatat fmtcase(what, SHOW_filename); 554 1.1 atatat fmtcase(what, SHOW_sizerdev); 555 1.1 atatat default: 556 1.1 atatat goto badfmt; 557 1.1 atatat } 558 1.5 atatat #undef fmtcasef 559 1.1 atatat #undef fmtcase 560 1.1 atatat 561 1.1 atatat t = format1(st, 562 1.1 atatat file, 563 1.1 atatat subfmt, statfmt - subfmt, 564 1.4 atatat buf, sizeof(buf), 565 1.34 christos flags, size, prec, ofmt, hilo, what, quiet); 566 1.1 atatat 567 1.28 lukem for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) 568 1.4 atatat addchar(stdout, buf[i], &nl); 569 1.1 atatat 570 1.1 atatat continue; 571 1.1 atatat 572 1.1 atatat badfmt: 573 1.1 atatat errx(1, "%.*s: bad format", 574 1.1 atatat (int)(statfmt - subfmt + 1), subfmt); 575 1.1 atatat } 576 1.1 atatat 577 1.1 atatat if (!nl && !nonl) 578 1.4 atatat (void)fputc('\n', stdout); 579 1.4 atatat (void)fflush(stdout); 580 1.1 atatat } 581 1.1 atatat 582 1.39 christos static const char * 583 1.39 christos fmttime(char *buf, size_t len, const char *fmt, time_t secs, long nsecs) 584 1.39 christos { 585 1.39 christos struct tm tm; 586 1.43 kre const char *fpb, *fp1, *fp2; /* pointers into fmt */ 587 1.43 kre char *fmt2 = NULL; /* replacement fmt (if not NULL) */ 588 1.43 kre /* XXX init of next twp for stupid gcc only */ 589 1.43 kre char *f2p = NULL; /* ptr into fmt2 - last added */ 590 1.43 kre size_t flen = 0; 591 1.43 kre size_t o; 592 1.43 kre int sl; 593 1.42 kre 594 1.39 christos if (localtime_r(&secs, &tm) == NULL) { 595 1.39 christos secs = 0; 596 1.39 christos (void)localtime_r(&secs, &tm); 597 1.39 christos } 598 1.43 kre for (fp1 = fpb = fmt; (fp2 = strchr(fp1, '%')) != NULL; ) { 599 1.43 kre if (fp2[1] != 'f') { 600 1.43 kre /* make sure we don't find the 2nd '%' in "%%" */ 601 1.43 kre fp1 = fp2 + 1 + (fp2[1] != '\0'); 602 1.43 kre continue; 603 1.43 kre } 604 1.43 kre if (fmt2 == NULL) { 605 1.43 kre /* allow for ~100 %f's in the format ... */ 606 1.43 kre flen = strlen(fmt) + 1024; 607 1.43 kre 608 1.43 kre if ((fmt2 = calloc(flen, 1)) == NULL) { 609 1.43 kre fp1 = fp2 + 2; 610 1.43 kre continue; 611 1.43 kre } 612 1.43 kre f2p = fmt2; 613 1.43 kre 614 1.43 kre o = (size_t)(fp2 - fpb); 615 1.43 kre memcpy(f2p, fpb, o); /* must fit */ 616 1.39 christos fmt = fmt2; 617 1.43 kre } else { 618 1.43 kre o = (size_t)(fp2 - fpb); 619 1.43 kre if (flen > o) 620 1.43 kre memcpy(f2p, fpb, o); 621 1.43 kre } 622 1.43 kre if (flen < o + 10) { /* 9 digits + \0 == 10 */ 623 1.43 kre *f2p = '\0'; 624 1.43 kre break; 625 1.43 kre } 626 1.43 kre f2p += o; 627 1.43 kre flen -= o; 628 1.43 kre sl = snprintf(f2p, flen, "%.9ld", nsecs); 629 1.43 kre if (sl == -1) 630 1.43 kre sl = 0; 631 1.43 kre f2p += sl; 632 1.43 kre *f2p = '\0'; 633 1.43 kre flen -= sl; 634 1.43 kre fp1 = fp2 + 2; 635 1.43 kre fpb = fp1; 636 1.43 kre } 637 1.43 kre if (fmt2 != NULL) { 638 1.43 kre o = strlen(fpb); 639 1.43 kre if (flen > o) { 640 1.43 kre memcpy(f2p, fpb, o); 641 1.43 kre f2p[o] = '\0'; 642 1.39 christos } 643 1.39 christos } 644 1.39 christos 645 1.39 christos (void)strftime(buf, len, fmt, &tm); 646 1.39 christos free(fmt2); 647 1.39 christos return buf; 648 1.39 christos } 649 1.39 christos 650 1.1 atatat /* 651 1.1 atatat * Arranges output according to a single parsed format substring. 652 1.1 atatat */ 653 1.35 joerg static int 654 1.1 atatat format1(const struct stat *st, 655 1.1 atatat const char *file, 656 1.1 atatat const char *fmt, int flen, 657 1.1 atatat char *buf, size_t blen, 658 1.1 atatat int flags, int size, int prec, int ofmt, 659 1.34 christos int hilo, int what, int quiet) 660 1.1 atatat { 661 1.39 christos uint64_t data; 662 1.28 lukem char *stmp, lfmt[24], tmp[20]; 663 1.28 lukem const char *sdata; 664 1.44 mrg char smode[12], sid[13], path[PATH_MAX + 4], visbuf[PATH_MAX * 4 + 4]; 665 1.1 atatat struct passwd *pw; 666 1.1 atatat struct group *gr; 667 1.14 chs time_t secs; 668 1.14 chs long nsecs; 669 1.36 apb int l; 670 1.36 apb int formats; /* bitmap of allowed formats for this datum */ 671 1.36 apb int small; /* true if datum is a small integer */ 672 1.36 apb int gottime; /* true if secs and nsecs are valid */ 673 1.36 apb int shift; /* powers of 2 to scale numbers before printing */ 674 1.36 apb size_t prefixlen; /* length of constant prefix for string data */ 675 1.1 atatat 676 1.1 atatat formats = 0; 677 1.1 atatat small = 0; 678 1.14 chs gottime = 0; 679 1.14 chs secs = 0; 680 1.14 chs nsecs = 0; 681 1.23 atatat shift = 0; 682 1.36 apb prefixlen = 0; 683 1.1 atatat 684 1.1 atatat /* 685 1.1 atatat * First, pick out the data and tweak it based on hilo or 686 1.1 atatat * specified output format (symlink output only). 687 1.1 atatat */ 688 1.1 atatat switch (what) { 689 1.1 atatat case SHOW_st_dev: 690 1.1 atatat case SHOW_st_rdev: 691 1.1 atatat small = (sizeof(st->st_dev) == 4); 692 1.1 atatat data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; 693 1.13 atatat #if HAVE_DEVNAME 694 1.1 atatat sdata = (what == SHOW_st_dev) ? 695 1.1 atatat devname(st->st_dev, S_IFBLK) : 696 1.1 atatat devname(st->st_rdev, 697 1.38 dsl S_ISCHR(st->st_mode) ? S_IFCHR : 698 1.38 dsl S_ISBLK(st->st_mode) ? S_IFBLK : 699 1.38 dsl 0U); 700 1.3 atatat if (sdata == NULL) 701 1.3 atatat sdata = "???"; 702 1.13 atatat #endif /* HAVE_DEVNAME */ 703 1.1 atatat if (hilo == HIGH_PIECE) { 704 1.1 atatat data = major(data); 705 1.1 atatat hilo = 0; 706 1.1 atatat } 707 1.1 atatat else if (hilo == LOW_PIECE) { 708 1.1 atatat data = minor((unsigned)data); 709 1.1 atatat hilo = 0; 710 1.1 atatat } 711 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 712 1.13 atatat #if HAVE_DEVNAME 713 1.5 atatat FMTF_STRING; 714 1.13 atatat #else /* HAVE_DEVNAME */ 715 1.13 atatat 0; 716 1.13 atatat #endif /* HAVE_DEVNAME */ 717 1.39 christos if (ofmt == 0) { 718 1.39 christos if (data == (uint64_t)-1) 719 1.39 christos ofmt = FMTF_DECIMAL; 720 1.39 christos else 721 1.39 christos ofmt = FMTF_UNSIGNED; 722 1.39 christos } 723 1.1 atatat break; 724 1.1 atatat case SHOW_st_ino: 725 1.1 atatat small = (sizeof(st->st_ino) == 4); 726 1.1 atatat data = st->st_ino; 727 1.1 atatat sdata = NULL; 728 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 729 1.1 atatat if (ofmt == 0) 730 1.5 atatat ofmt = FMTF_UNSIGNED; 731 1.1 atatat break; 732 1.1 atatat case SHOW_st_mode: 733 1.1 atatat small = (sizeof(st->st_mode) == 4); 734 1.1 atatat data = st->st_mode; 735 1.1 atatat strmode(st->st_mode, smode); 736 1.28 lukem stmp = smode; 737 1.28 lukem l = strlen(stmp); 738 1.28 lukem if (stmp[l - 1] == ' ') 739 1.28 lukem stmp[--l] = '\0'; 740 1.1 atatat if (hilo == HIGH_PIECE) { 741 1.1 atatat data >>= 12; 742 1.28 lukem stmp += 1; 743 1.28 lukem stmp[3] = '\0'; 744 1.1 atatat hilo = 0; 745 1.1 atatat } 746 1.1 atatat else if (hilo == MIDDLE_PIECE) { 747 1.2 atatat data = (data >> 9) & 07; 748 1.28 lukem stmp += 4; 749 1.28 lukem stmp[3] = '\0'; 750 1.1 atatat hilo = 0; 751 1.1 atatat } 752 1.1 atatat else if (hilo == LOW_PIECE) { 753 1.2 atatat data &= 0777; 754 1.28 lukem stmp += 7; 755 1.28 lukem stmp[3] = '\0'; 756 1.1 atatat hilo = 0; 757 1.1 atatat } 758 1.28 lukem sdata = stmp; 759 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 760 1.5 atatat FMTF_STRING; 761 1.1 atatat if (ofmt == 0) 762 1.5 atatat ofmt = FMTF_OCTAL; 763 1.1 atatat break; 764 1.1 atatat case SHOW_st_nlink: 765 1.1 atatat small = (sizeof(st->st_dev) == 4); 766 1.1 atatat data = st->st_nlink; 767 1.1 atatat sdata = NULL; 768 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 769 1.1 atatat if (ofmt == 0) 770 1.5 atatat ofmt = FMTF_UNSIGNED; 771 1.1 atatat break; 772 1.1 atatat case SHOW_st_uid: 773 1.1 atatat small = (sizeof(st->st_uid) == 4); 774 1.1 atatat data = st->st_uid; 775 1.1 atatat if ((pw = getpwuid(st->st_uid)) != NULL) 776 1.1 atatat sdata = pw->pw_name; 777 1.1 atatat else { 778 1.1 atatat snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); 779 1.1 atatat sdata = sid; 780 1.1 atatat } 781 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 782 1.5 atatat FMTF_STRING; 783 1.1 atatat if (ofmt == 0) 784 1.5 atatat ofmt = FMTF_UNSIGNED; 785 1.1 atatat break; 786 1.1 atatat case SHOW_st_gid: 787 1.1 atatat small = (sizeof(st->st_gid) == 4); 788 1.1 atatat data = st->st_gid; 789 1.1 atatat if ((gr = getgrgid(st->st_gid)) != NULL) 790 1.1 atatat sdata = gr->gr_name; 791 1.1 atatat else { 792 1.1 atatat snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); 793 1.1 atatat sdata = sid; 794 1.1 atatat } 795 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 796 1.5 atatat FMTF_STRING; 797 1.1 atatat if (ofmt == 0) 798 1.5 atatat ofmt = FMTF_UNSIGNED; 799 1.1 atatat break; 800 1.1 atatat case SHOW_st_atime: 801 1.14 chs gottime = 1; 802 1.14 chs secs = st->st_atime; 803 1.16 lukem #if HAVE_STRUCT_STAT_ST_MTIMENSEC 804 1.14 chs nsecs = st->st_atimensec; 805 1.14 chs #endif 806 1.1 atatat /* FALLTHROUGH */ 807 1.1 atatat case SHOW_st_mtime: 808 1.14 chs if (!gottime) { 809 1.17 atatat gottime = 1; 810 1.14 chs secs = st->st_mtime; 811 1.16 lukem #if HAVE_STRUCT_STAT_ST_MTIMENSEC 812 1.14 chs nsecs = st->st_mtimensec; 813 1.14 chs #endif 814 1.14 chs } 815 1.1 atatat /* FALLTHROUGH */ 816 1.1 atatat case SHOW_st_ctime: 817 1.14 chs if (!gottime) { 818 1.17 atatat gottime = 1; 819 1.14 chs secs = st->st_ctime; 820 1.16 lukem #if HAVE_STRUCT_STAT_ST_MTIMENSEC 821 1.14 chs nsecs = st->st_ctimensec; 822 1.14 chs #endif 823 1.14 chs } 824 1.45 mrg #if HAVE_STRUCT_STAT_ST_BIRTHTIME 825 1.10 atatat /* FALLTHROUGH */ 826 1.10 atatat case SHOW_st_btime: 827 1.14 chs if (!gottime) { 828 1.17 atatat gottime = 1; 829 1.19 jmc secs = st->st_birthtime; 830 1.21 jmc #if HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC 831 1.19 jmc nsecs = st->st_birthtimensec; 832 1.21 jmc #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC */ 833 1.14 chs } 834 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ 835 1.14 chs small = (sizeof(secs) == 4); 836 1.14 chs data = secs; 837 1.39 christos sdata = fmttime(path, sizeof(path), timefmt, secs, nsecs); 838 1.39 christos 839 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | 840 1.5 atatat FMTF_FLOAT | FMTF_STRING; 841 1.1 atatat if (ofmt == 0) 842 1.5 atatat ofmt = FMTF_DECIMAL; 843 1.1 atatat break; 844 1.1 atatat case SHOW_st_size: 845 1.1 atatat small = (sizeof(st->st_size) == 4); 846 1.1 atatat data = st->st_size; 847 1.1 atatat sdata = NULL; 848 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 849 1.1 atatat if (ofmt == 0) 850 1.5 atatat ofmt = FMTF_UNSIGNED; 851 1.23 atatat switch (hilo) { 852 1.23 atatat case HIGH_PIECE: 853 1.23 atatat shift = 30; /* gigabytes */ 854 1.23 atatat hilo = 0; 855 1.23 atatat break; 856 1.23 atatat case MIDDLE_PIECE: 857 1.23 atatat shift = 20; /* megabytes */ 858 1.23 atatat hilo = 0; 859 1.23 atatat break; 860 1.23 atatat case LOW_PIECE: 861 1.23 atatat shift = 10; /* kilobytes */ 862 1.23 atatat hilo = 0; 863 1.23 atatat break; 864 1.23 atatat } 865 1.1 atatat break; 866 1.1 atatat case SHOW_st_blocks: 867 1.1 atatat small = (sizeof(st->st_blocks) == 4); 868 1.1 atatat data = st->st_blocks; 869 1.1 atatat sdata = NULL; 870 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 871 1.1 atatat if (ofmt == 0) 872 1.5 atatat ofmt = FMTF_UNSIGNED; 873 1.1 atatat break; 874 1.1 atatat case SHOW_st_blksize: 875 1.1 atatat small = (sizeof(st->st_blksize) == 4); 876 1.1 atatat data = st->st_blksize; 877 1.1 atatat sdata = NULL; 878 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 879 1.1 atatat if (ofmt == 0) 880 1.5 atatat ofmt = FMTF_UNSIGNED; 881 1.1 atatat break; 882 1.11 lukem #if HAVE_STRUCT_STAT_ST_FLAGS 883 1.1 atatat case SHOW_st_flags: 884 1.1 atatat small = (sizeof(st->st_flags) == 4); 885 1.1 atatat data = st->st_flags; 886 1.50 christos formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 887 1.50 christos #if !HAVE_NBTOOL_CONFIG_H 888 1.49 christos sdata = flags_to_string((u_long)st->st_flags, "-"); 889 1.50 christos formats |= FMT_STRING; 890 1.50 christos #endif 891 1.1 atatat if (ofmt == 0) 892 1.5 atatat ofmt = FMTF_UNSIGNED; 893 1.1 atatat break; 894 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_FLAGS */ 895 1.13 atatat #if HAVE_STRUCT_STAT_ST_GEN 896 1.1 atatat case SHOW_st_gen: 897 1.1 atatat small = (sizeof(st->st_gen) == 4); 898 1.1 atatat data = st->st_gen; 899 1.1 atatat sdata = NULL; 900 1.5 atatat formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; 901 1.1 atatat if (ofmt == 0) 902 1.5 atatat ofmt = FMTF_UNSIGNED; 903 1.1 atatat break; 904 1.13 atatat #endif /* HAVE_STRUCT_STAT_ST_GEN */ 905 1.24 elad case SHOW_realpath: 906 1.24 elad small = 0; 907 1.24 elad data = 0; 908 1.25 mlelstv if (file == NULL) { 909 1.31 dholland (void)strlcpy(path, "(stdin)", sizeof(path)); 910 1.25 mlelstv sdata = path; 911 1.25 mlelstv } else { 912 1.25 mlelstv snprintf(path, sizeof(path), " -> "); 913 1.25 mlelstv if (realpath(file, path + 4) == NULL) { 914 1.34 christos if (!quiet) 915 1.34 christos warn("realpath `%s'", file); 916 1.25 mlelstv linkfail = 1; 917 1.25 mlelstv l = 0; 918 1.25 mlelstv path[0] = '\0'; 919 1.25 mlelstv } 920 1.27 atatat sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 921 1.36 apb prefixlen = (ofmt == FMTF_STRING ? 4 : 0); 922 1.24 elad } 923 1.24 elad 924 1.24 elad formats = FMTF_STRING; 925 1.24 elad if (ofmt == 0) 926 1.24 elad ofmt = FMTF_STRING; 927 1.24 elad break; 928 1.1 atatat case SHOW_symlink: 929 1.1 atatat small = 0; 930 1.1 atatat data = 0; 931 1.1 atatat if (S_ISLNK(st->st_mode)) { 932 1.1 atatat snprintf(path, sizeof(path), " -> "); 933 1.9 provos l = readlink(file, path + 4, sizeof(path) - 4 - 1); 934 1.1 atatat if (l == -1) { 935 1.34 christos if (!quiet) 936 1.34 christos warn("readlink `%s'", file); 937 1.4 atatat linkfail = 1; 938 1.1 atatat l = 0; 939 1.1 atatat path[0] = '\0'; 940 1.1 atatat } 941 1.1 atatat path[l + 4] = '\0'; 942 1.5 atatat sdata = path + (ofmt == FMTF_STRING ? 0 : 4); 943 1.36 apb prefixlen = (ofmt == FMTF_STRING ? 4 : 0); 944 1.1 atatat } 945 1.4 atatat else { 946 1.4 atatat linkfail = 1; 947 1.1 atatat sdata = ""; 948 1.4 atatat } 949 1.5 atatat formats = FMTF_STRING; 950 1.1 atatat if (ofmt == 0) 951 1.5 atatat ofmt = FMTF_STRING; 952 1.1 atatat break; 953 1.1 atatat case SHOW_filetype: 954 1.1 atatat small = 0; 955 1.1 atatat data = 0; 956 1.28 lukem sdata = ""; 957 1.1 atatat if (hilo == 0 || hilo == LOW_PIECE) { 958 1.1 atatat switch (st->st_mode & S_IFMT) { 959 1.28 lukem case S_IFIFO: sdata = "|"; break; 960 1.28 lukem case S_IFDIR: sdata = "/"; break; 961 1.1 atatat case S_IFREG: 962 1.1 atatat if (st->st_mode & 963 1.1 atatat (S_IXUSR | S_IXGRP | S_IXOTH)) 964 1.28 lukem sdata = "*"; 965 1.1 atatat break; 966 1.28 lukem case S_IFLNK: sdata = "@"; break; 967 1.19 jmc #ifdef S_IFSOCK 968 1.28 lukem case S_IFSOCK: sdata = "="; break; 969 1.19 jmc #endif 970 1.13 atatat #ifdef S_IFWHT 971 1.28 lukem case S_IFWHT: sdata = "%"; break; 972 1.13 atatat #endif /* S_IFWHT */ 973 1.13 atatat #ifdef S_IFDOOR 974 1.28 lukem case S_IFDOOR: sdata = ">"; break; 975 1.13 atatat #endif /* S_IFDOOR */ 976 1.1 atatat } 977 1.1 atatat hilo = 0; 978 1.1 atatat } 979 1.1 atatat else if (hilo == HIGH_PIECE) { 980 1.1 atatat switch (st->st_mode & S_IFMT) { 981 1.1 atatat case S_IFIFO: sdata = "Fifo File"; break; 982 1.1 atatat case S_IFCHR: sdata = "Character Device"; break; 983 1.1 atatat case S_IFDIR: sdata = "Directory"; break; 984 1.1 atatat case S_IFBLK: sdata = "Block Device"; break; 985 1.1 atatat case S_IFREG: sdata = "Regular File"; break; 986 1.1 atatat case S_IFLNK: sdata = "Symbolic Link"; break; 987 1.19 jmc #ifdef S_IFSOCK 988 1.1 atatat case S_IFSOCK: sdata = "Socket"; break; 989 1.19 jmc #endif 990 1.13 atatat #ifdef S_IFWHT 991 1.1 atatat case S_IFWHT: sdata = "Whiteout File"; break; 992 1.13 atatat #endif /* S_IFWHT */ 993 1.13 atatat #ifdef S_IFDOOR 994 1.13 atatat case S_IFDOOR: sdata = "Door"; break; 995 1.13 atatat #endif /* S_IFDOOR */ 996 1.1 atatat default: sdata = "???"; break; 997 1.1 atatat } 998 1.1 atatat hilo = 0; 999 1.1 atatat } 1000 1.5 atatat formats = FMTF_STRING; 1001 1.1 atatat if (ofmt == 0) 1002 1.5 atatat ofmt = FMTF_STRING; 1003 1.1 atatat break; 1004 1.1 atatat case SHOW_filename: 1005 1.1 atatat small = 0; 1006 1.1 atatat data = 0; 1007 1.20 atatat if (file == NULL) { 1008 1.31 dholland (void)strlcpy(path, "(stdin)", sizeof(path)); 1009 1.20 atatat if (hilo == HIGH_PIECE || hilo == LOW_PIECE) 1010 1.20 atatat hilo = 0; 1011 1.20 atatat } 1012 1.20 atatat else if (hilo == 0) 1013 1.31 dholland (void)strlcpy(path, file, sizeof(path)); 1014 1.20 atatat else { 1015 1.20 atatat char *s; 1016 1.31 dholland (void)strlcpy(path, file, sizeof(path)); 1017 1.20 atatat s = strrchr(path, '/'); 1018 1.20 atatat if (s != NULL) { 1019 1.20 atatat /* trim off trailing /'s */ 1020 1.20 atatat while (s != path && 1021 1.20 atatat s[0] == '/' && s[1] == '\0') 1022 1.20 atatat *s-- = '\0'; 1023 1.20 atatat s = strrchr(path, '/'); 1024 1.20 atatat } 1025 1.20 atatat if (hilo == HIGH_PIECE) { 1026 1.20 atatat if (s == NULL) 1027 1.31 dholland (void)strlcpy(path, ".", sizeof(path)); 1028 1.20 atatat else { 1029 1.20 atatat while (s != path && s[0] == '/') 1030 1.20 atatat *s-- = '\0'; 1031 1.20 atatat } 1032 1.20 atatat hilo = 0; 1033 1.20 atatat } 1034 1.20 atatat else if (hilo == LOW_PIECE) { 1035 1.20 atatat if (s != NULL && s[1] != '\0') 1036 1.31 dholland (void)strlcpy(path, s + 1, 1037 1.20 atatat sizeof(path)); 1038 1.20 atatat hilo = 0; 1039 1.20 atatat } 1040 1.20 atatat } 1041 1.1 atatat sdata = path; 1042 1.5 atatat formats = FMTF_STRING; 1043 1.1 atatat if (ofmt == 0) 1044 1.5 atatat ofmt = FMTF_STRING; 1045 1.1 atatat break; 1046 1.1 atatat case SHOW_sizerdev: 1047 1.1 atatat if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 1048 1.1 atatat char majdev[20], mindev[20]; 1049 1.1 atatat int l1, l2; 1050 1.1 atatat 1051 1.48 kre if (size == 0) /* avoid -1/2 */ 1052 1.48 kre size++; /* 1/2 == 0/2 so this is safe */ 1053 1.1 atatat l1 = format1(st, 1054 1.1 atatat file, 1055 1.1 atatat fmt, flen, 1056 1.1 atatat majdev, sizeof(majdev), 1057 1.48 kre flags, (size - 1) / 2, prec, 1058 1.34 christos ofmt, HIGH_PIECE, SHOW_st_rdev, quiet); 1059 1.1 atatat l2 = format1(st, 1060 1.1 atatat file, 1061 1.1 atatat fmt, flen, 1062 1.1 atatat mindev, sizeof(mindev), 1063 1.48 kre flags | FLAG_MINUS , size / 2, prec, 1064 1.34 christos ofmt, LOW_PIECE, SHOW_st_rdev, quiet); 1065 1.1 atatat return (snprintf(buf, blen, "%.*s,%.*s", 1066 1.1 atatat l1, majdev, l2, mindev)); 1067 1.1 atatat } 1068 1.1 atatat else { 1069 1.1 atatat return (format1(st, 1070 1.1 atatat file, 1071 1.1 atatat fmt, flen, 1072 1.1 atatat buf, blen, 1073 1.1 atatat flags, size, prec, 1074 1.34 christos ofmt, 0, SHOW_st_size, quiet)); 1075 1.1 atatat } 1076 1.1 atatat /*NOTREACHED*/ 1077 1.1 atatat default: 1078 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt); 1079 1.1 atatat } 1080 1.1 atatat 1081 1.53 rillig if (hilo != 0 // subdatum not supported 1082 1.53 rillig || !(ofmt & formats) // output format not supported 1083 1.53 rillig || (ofmt == FMTF_STRING && flags & FLAG_SPACE) 1084 1.53 rillig || (ofmt == FMTF_STRING && flags & FLAG_PLUS) 1085 1.53 rillig || (ofmt == FMTF_STRING && flags & FLAG_ZERO)) 1086 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt); 1087 1.1 atatat 1088 1.1 atatat /* 1089 1.36 apb * FLAG_POUND with FMTF_STRING means use vis(3) encoding. 1090 1.36 apb * First prefixlen chars are not encoded. 1091 1.36 apb */ 1092 1.36 apb if ((flags & FLAG_POUND) != 0 && ofmt == FMTF_STRING) { 1093 1.52 kre flags &= ~FLAG_POUND; 1094 1.36 apb strncpy(visbuf, sdata, prefixlen); 1095 1.46 mrg /* Avoid GCC warnings. */ 1096 1.46 mrg visbuf[prefixlen] = 0; 1097 1.36 apb strnvis(visbuf + prefixlen, sizeof(visbuf) - prefixlen, 1098 1.36 apb sdata + prefixlen, VIS_WHITE | VIS_OCTAL | VIS_CSTYLE); 1099 1.36 apb sdata = visbuf; 1100 1.36 apb } 1101 1.36 apb 1102 1.36 apb /* 1103 1.1 atatat * Assemble the format string for passing to printf(3). 1104 1.1 atatat */ 1105 1.1 atatat lfmt[0] = '\0'; 1106 1.1 atatat (void)strcat(lfmt, "%"); 1107 1.2 atatat if (flags & FLAG_POUND) 1108 1.1 atatat (void)strcat(lfmt, "#"); 1109 1.2 atatat if (flags & FLAG_SPACE) 1110 1.1 atatat (void)strcat(lfmt, " "); 1111 1.2 atatat if (flags & FLAG_PLUS) 1112 1.1 atatat (void)strcat(lfmt, "+"); 1113 1.2 atatat if (flags & FLAG_MINUS) 1114 1.1 atatat (void)strcat(lfmt, "-"); 1115 1.2 atatat if (flags & FLAG_ZERO) 1116 1.1 atatat (void)strcat(lfmt, "0"); 1117 1.1 atatat 1118 1.1 atatat /* 1119 1.1 atatat * Only the timespecs support the FLOAT output format, and that 1120 1.1 atatat * requires work that differs from the other formats. 1121 1.1 atatat */ 1122 1.5 atatat if (ofmt == FMTF_FLOAT) { 1123 1.1 atatat /* 1124 1.1 atatat * Nothing after the decimal point, so just print seconds. 1125 1.1 atatat */ 1126 1.1 atatat if (prec == 0) { 1127 1.1 atatat if (size != -1) { 1128 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size); 1129 1.1 atatat (void)strcat(lfmt, tmp); 1130 1.1 atatat } 1131 1.29 dholland (void)strcat(lfmt, "lld"); 1132 1.29 dholland return (snprintf(buf, blen, lfmt, 1133 1.29 dholland (long long)secs)); 1134 1.1 atatat } 1135 1.1 atatat 1136 1.1 atatat /* 1137 1.1 atatat * Unspecified precision gets all the precision we have: 1138 1.1 atatat * 9 digits. 1139 1.1 atatat */ 1140 1.1 atatat if (prec == -1) 1141 1.1 atatat prec = 9; 1142 1.1 atatat 1143 1.1 atatat /* 1144 1.1 atatat * Adjust the size for the decimal point and the digits 1145 1.1 atatat * that will follow. 1146 1.1 atatat */ 1147 1.1 atatat size -= prec + 1; 1148 1.1 atatat 1149 1.1 atatat /* 1150 1.1 atatat * Any leftover size that's legitimate will be used. 1151 1.1 atatat */ 1152 1.1 atatat if (size > 0) { 1153 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size); 1154 1.1 atatat (void)strcat(lfmt, tmp); 1155 1.1 atatat } 1156 1.30 dholland /* Seconds: time_t cast to long long. */ 1157 1.29 dholland (void)strcat(lfmt, "lld"); 1158 1.1 atatat 1159 1.1 atatat /* 1160 1.1 atatat * The stuff after the decimal point always needs zero 1161 1.1 atatat * filling. 1162 1.1 atatat */ 1163 1.1 atatat (void)strcat(lfmt, ".%0"); 1164 1.1 atatat 1165 1.1 atatat /* 1166 1.1 atatat * We can "print" at most nine digits of precision. The 1167 1.1 atatat * rest we will pad on at the end. 1168 1.30 dholland * 1169 1.30 dholland * Nanoseconds: long. 1170 1.1 atatat */ 1171 1.29 dholland (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); 1172 1.1 atatat (void)strcat(lfmt, tmp); 1173 1.1 atatat 1174 1.1 atatat /* 1175 1.1 atatat * For precision of less that nine digits, trim off the 1176 1.1 atatat * less significant figures. 1177 1.1 atatat */ 1178 1.1 atatat for (; prec < 9; prec++) 1179 1.14 chs nsecs /= 10; 1180 1.1 atatat 1181 1.1 atatat /* 1182 1.1 atatat * Use the format, and then tack on any zeroes that 1183 1.1 atatat * might be required to make up the requested precision. 1184 1.1 atatat */ 1185 1.29 dholland l = snprintf(buf, blen, lfmt, (long long)secs, nsecs); 1186 1.28 lukem for (; prec > 9 && l < (int)blen; prec--, l++) 1187 1.1 atatat (void)strcat(buf, "0"); 1188 1.1 atatat return (l); 1189 1.1 atatat } 1190 1.1 atatat 1191 1.1 atatat /* 1192 1.1 atatat * Add on size and precision, if specified, to the format. 1193 1.1 atatat */ 1194 1.1 atatat if (size != -1) { 1195 1.1 atatat (void)snprintf(tmp, sizeof(tmp), "%d", size); 1196 1.1 atatat (void)strcat(lfmt, tmp); 1197 1.1 atatat } 1198 1.1 atatat if (prec != -1) { 1199 1.1 atatat (void)snprintf(tmp, sizeof(tmp), ".%d", prec); 1200 1.1 atatat (void)strcat(lfmt, tmp); 1201 1.1 atatat } 1202 1.1 atatat 1203 1.1 atatat /* 1204 1.1 atatat * String output uses the temporary sdata. 1205 1.1 atatat */ 1206 1.5 atatat if (ofmt == FMTF_STRING) { 1207 1.1 atatat if (sdata == NULL) 1208 1.1 atatat errx(1, "%.*s: bad format", (int)flen, fmt); 1209 1.1 atatat (void)strcat(lfmt, "s"); 1210 1.1 atatat return (snprintf(buf, blen, lfmt, sdata)); 1211 1.1 atatat } 1212 1.1 atatat 1213 1.1 atatat /* 1214 1.1 atatat * Ensure that sign extension does not cause bad looking output 1215 1.1 atatat * for some forms. 1216 1.1 atatat */ 1217 1.5 atatat if (small && ofmt != FMTF_DECIMAL) 1218 1.39 christos data = (uint32_t)data; 1219 1.1 atatat 1220 1.1 atatat /* 1221 1.1 atatat * The four "numeric" output forms. 1222 1.1 atatat */ 1223 1.1 atatat (void)strcat(lfmt, "ll"); 1224 1.1 atatat switch (ofmt) { 1225 1.5 atatat case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; 1226 1.23 atatat case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; 1227 1.5 atatat case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; 1228 1.5 atatat case FMTF_HEX: (void)strcat(lfmt, "x"); break; 1229 1.1 atatat } 1230 1.1 atatat 1231 1.23 atatat /* 1232 1.23 atatat * shift and round to nearest for kilobytes, megabytes, 1233 1.23 atatat * gigabytes. 1234 1.23 atatat */ 1235 1.23 atatat if (shift > 0) { 1236 1.23 atatat data >>= (shift - 1); 1237 1.23 atatat data++; 1238 1.23 atatat data >>= 1; 1239 1.23 atatat } 1240 1.23 atatat 1241 1.1 atatat return (snprintf(buf, blen, lfmt, data)); 1242 1.1 atatat } 1243