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