1 /* 2 * Copyright (c) 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Michael Fischbein. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)print.c 8.4 (Berkeley) 4/17/94 33 * $FreeBSD: src/bin/ls/print.c,v 1.71 2004/05/02 11:25:37 tjr Exp $ 34 * $DragonFly: src/bin/ls/print.c,v 1.14 2005/09/18 18:35:23 asmodai Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/stat.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <fts.h> 43 #include <math.h> 44 #include <langinfo.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <time.h> 49 #include <unistd.h> 50 #ifdef COLORLS 51 #include <ctype.h> 52 #include <termcap.h> 53 #include <signal.h> 54 #endif 55 56 #include "ls.h" 57 #include "extern.h" 58 59 static int printaname(const FTSENT *, u_long, u_long); 60 static void printlink(const FTSENT *); 61 static void printtime(time_t); 62 static int printtype(u_int); 63 static void printsize(size_t, off_t); 64 #ifdef COLORLS 65 static void endcolor(int); 66 static int colortype(mode_t); 67 #endif 68 69 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 70 71 #define KILO_SZ(n) (n) 72 #define MEGA_SZ(n) ((n) * (n)) 73 #define GIGA_SZ(n) ((n) * (n) * (n)) 74 #define TERA_SZ(n) ((n) * (n) * (n) * (n)) 75 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 76 77 #define KILO_2_SZ (KILO_SZ(1024ULL)) 78 #define MEGA_2_SZ (MEGA_SZ(1024ULL)) 79 #define GIGA_2_SZ (GIGA_SZ(1024ULL)) 80 #define TERA_2_SZ (TERA_SZ(1024ULL)) 81 #define PETA_2_SZ (PETA_SZ(1024ULL)) 82 83 static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 84 85 typedef enum { 86 NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX 87 } unit_t; 88 static unit_t unit_adjust(off_t *); 89 90 static unit_t unitp[] = {NONE, KILO, MEGA, GIGA, TERA, PETA}; 91 92 #ifdef COLORLS 93 /* Most of these are taken from <sys/stat.h> */ 94 typedef enum Colors { 95 C_DIR, /* directory */ 96 C_LNK, /* symbolic link */ 97 C_SOCK, /* socket */ 98 C_FIFO, /* pipe */ 99 C_EXEC, /* executable */ 100 C_BLK, /* block special */ 101 C_CHR, /* character special */ 102 C_SUID, /* setuid executable */ 103 C_SGID, /* setgid executable */ 104 C_WSDIR, /* directory writeble to others, with sticky 105 * bit */ 106 C_WDIR, /* directory writeble to others, without 107 * sticky bit */ 108 C_NUMCOLORS /* just a place-holder */ 109 } Colors; 110 111 static const char *defcolors = "exfxcxdxbxegedabagacad"; 112 113 /* colors for file types */ 114 static struct { 115 int num[2]; 116 int bold; 117 } colors[C_NUMCOLORS]; 118 #endif 119 120 void 121 printscol(const DISPLAY *dp) 122 { 123 FTSENT *p; 124 125 for (p = dp->list; p; p = p->fts_link) { 126 if (IS_NOPRINT(p)) 127 continue; 128 printaname(p, dp->s_inode, dp->s_block); 129 putchar('\n'); 130 } 131 } 132 133 /* 134 * print name in current style 135 */ 136 int 137 printname(const char *name) 138 { 139 if (f_octal || f_octal_escape) 140 return prn_octal(name); 141 else if (f_nonprint) 142 return prn_printable(name); 143 else 144 return prn_normal(name); 145 } 146 147 void 148 printlong(const DISPLAY *dp) 149 { 150 struct stat *sp; 151 FTSENT *p; 152 NAMES *np; 153 char buf[20]; 154 #ifdef COLORLS 155 int color_printed = 0; 156 #endif 157 158 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) 159 printf("total %lu\n", howmany(dp->btotal, blocksize)); 160 161 for (p = dp->list; p; p = p->fts_link) { 162 if (IS_NOPRINT(p)) 163 continue; 164 sp = p->fts_statp; 165 if (f_inode) 166 printf("%*lu ", dp->s_inode, (u_long)sp->st_ino); 167 if (f_size) 168 printf("%*lld ", 169 dp->s_block, howmany(sp->st_blocks, blocksize)); 170 strmode(sp->st_mode, buf); 171 np = p->fts_pointer; 172 printf("%s %*u %-*s %-*s ", buf, dp->s_nlink, 173 sp->st_nlink, dp->s_user, np->user, dp->s_group, 174 np->group); 175 if (f_fsmid) 176 printf("%s ", np->fsmid); 177 if (f_flags) 178 printf("%-*s ", dp->s_flags, np->flags); 179 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 180 if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) 181 printf("%3d, 0x%08x ", 182 major(sp->st_rdev), 183 (u_int)minor(sp->st_rdev)); 184 else 185 printf("%3d, %3d ", 186 major(sp->st_rdev), minor(sp->st_rdev)); 187 else if (dp->bcfile) 188 printf("%*s%*lld ", 189 8 - dp->s_size, "", dp->s_size, sp->st_size); 190 else 191 printsize(dp->s_size, sp->st_size); 192 if (f_accesstime) 193 printtime(sp->st_atime); 194 else if (f_statustime) 195 printtime(sp->st_ctime); 196 else 197 printtime(sp->st_mtime); 198 #ifdef COLORLS 199 if (f_color) 200 color_printed = colortype(sp->st_mode); 201 #endif 202 printname(p->fts_name); 203 #ifdef COLORLS 204 if (f_color && color_printed) 205 endcolor(0); 206 #endif 207 if (f_type) 208 printtype(sp->st_mode); 209 if (S_ISLNK(sp->st_mode)) 210 printlink(p); 211 putchar('\n'); 212 } 213 } 214 215 void 216 printstream(const DISPLAY *dp) 217 { 218 FTSENT *p; 219 int chcnt; 220 221 for (p = dp->list, chcnt = 0; p; p = p->fts_link) { 222 if (p->fts_number == NO_PRINT) 223 continue; 224 /* XXX strlen does not take octal escapes into account. */ 225 if (strlen(p->fts_name) + chcnt + 226 (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { 227 putchar('\n'); 228 chcnt = 0; 229 } 230 chcnt += printaname(p, dp->s_inode, dp->s_block); 231 if (p->fts_link) { 232 printf(", "); 233 chcnt += 2; 234 } 235 } 236 if (chcnt) 237 putchar('\n'); 238 } 239 240 void 241 printcol(const DISPLAY *dp) 242 { 243 static FTSENT **array; 244 static int lastentries = -1; 245 FTSENT *p; 246 FTSENT **narray; 247 int base; 248 int chcnt; 249 int cnt; 250 int col; 251 int colwidth; 252 int endcol; 253 int num; 254 int numcols; 255 int numrows; 256 int row; 257 int tabwidth; 258 259 if (f_notabs) 260 tabwidth = 1; 261 else 262 tabwidth = 8; 263 264 /* 265 * Have to do random access in the linked list -- build a table 266 * of pointers. 267 */ 268 if (dp->entries > lastentries) { 269 lastentries = dp->entries; 270 if ((narray = 271 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 272 warn(NULL); 273 printscol(dp); 274 return; 275 } 276 lastentries = dp->entries; 277 array = narray; 278 } 279 for (p = dp->list, num = 0; p; p = p->fts_link) 280 if (p->fts_number != NO_PRINT) 281 array[num++] = p; 282 283 colwidth = dp->maxlen; 284 if (f_inode) 285 colwidth += dp->s_inode + 1; 286 if (f_size) 287 colwidth += dp->s_block + 1; 288 if (f_type) 289 colwidth += 1; 290 291 colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); 292 if (termwidth < 2 * colwidth) { 293 printscol(dp); 294 return; 295 } 296 numcols = termwidth / colwidth; 297 numrows = num / numcols; 298 if (num % numcols) 299 ++numrows; 300 301 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) 302 printf("total %lu\n", howmany(dp->btotal, blocksize)); 303 304 /* counter if f_sortacross, else case-by-case */ 305 base = 0; 306 307 for (row = 0; row < numrows; ++row) { 308 endcol = colwidth; 309 if (!f_sortacross) 310 base = row; 311 for (col = 0, chcnt = 0; col < numcols; ++col) { 312 chcnt += printaname(array[base], dp->s_inode, 313 dp->s_block); 314 if (f_sortacross) 315 base++; 316 else 317 base += numrows; 318 if (base >= num) 319 break; 320 while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) 321 <= endcol) { 322 if (f_sortacross && col + 1 >= numcols) 323 break; 324 putchar(f_notabs ? ' ' : '\t'); 325 chcnt = cnt; 326 } 327 endcol += colwidth; 328 } 329 putchar('\n'); 330 } 331 } 332 333 /* 334 * print [inode] [size] name 335 * return # of characters printed, no trailing characters. 336 */ 337 static int 338 printaname(const FTSENT *p, u_long inodefield, u_long sizefield) 339 { 340 struct stat *sp; 341 int chcnt; 342 #ifdef COLORLS 343 int color_printed = 0; 344 #endif 345 346 sp = p->fts_statp; 347 chcnt = 0; 348 if (f_inode) 349 chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino); 350 if (f_size) 351 chcnt += printf("%*lld ", 352 (int)sizefield, howmany(sp->st_blocks, blocksize)); 353 #ifdef COLORLS 354 if (f_color) 355 color_printed = colortype(sp->st_mode); 356 #endif 357 chcnt += printname(p->fts_name); 358 #ifdef COLORLS 359 if (f_color && color_printed) 360 endcolor(0); 361 #endif 362 if (f_type) 363 chcnt += printtype(sp->st_mode); 364 return (chcnt); 365 } 366 367 static void 368 printtime(time_t ftime) 369 { 370 char longstring[80]; 371 static time_t now; 372 const char *format; 373 static int d_first = -1; 374 375 if (d_first < 0) 376 d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 377 if (now == 0) 378 now = time(NULL); 379 380 #define SIXMONTHS ((365 / 2) * 86400) 381 if (f_sectime) 382 /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ 383 format = d_first ? "%e %b %T %Y " : "%b %e %T %Y "; 384 else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) 385 /* mmm dd hh:mm || dd mmm hh:mm */ 386 format = d_first ? "%e %b %R " : "%b %e %R "; 387 else 388 /* mmm dd yyyy || dd mmm yyyy */ 389 format = d_first ? "%e %b %Y " : "%b %e %Y "; 390 strftime(longstring, sizeof(longstring), format, localtime(&ftime)); 391 fputs(longstring, stdout); 392 } 393 394 static int 395 printtype(u_int mode) 396 { 397 398 if (f_slash) { 399 if ((mode & S_IFMT) == S_IFDIR) { 400 putchar('/'); 401 return (1); 402 } 403 return (0); 404 } 405 406 switch (mode & S_IFMT) { 407 case S_IFDIR: 408 putchar('/'); 409 return (1); 410 case S_IFIFO: 411 putchar('|'); 412 return (1); 413 case S_IFLNK: 414 putchar('@'); 415 return (1); 416 case S_IFSOCK: 417 putchar('='); 418 return (1); 419 case S_IFWHT: 420 putchar('%'); 421 return (1); 422 default: 423 break; 424 } 425 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 426 putchar('*'); 427 return (1); 428 } 429 return (0); 430 } 431 432 #ifdef COLORLS 433 static int 434 putch(int c) 435 { 436 putchar(c); 437 return 0; 438 } 439 440 static int 441 writech(int c) 442 { 443 char tmp = (char)c; 444 445 write(STDOUT_FILENO, &tmp, 1); 446 return 0; 447 } 448 449 static void 450 printcolor(Colors c) 451 { 452 char *ansiseq; 453 454 if (colors[c].bold) 455 tputs(enter_bold, 1, putch); 456 457 if (colors[c].num[0] != -1) { 458 ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); 459 if (ansiseq) 460 tputs(ansiseq, 1, putch); 461 } 462 if (colors[c].num[1] != -1) { 463 ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); 464 if (ansiseq) 465 tputs(ansiseq, 1, putch); 466 } 467 } 468 469 static void 470 endcolor(int sig) 471 { 472 tputs(ansi_coloff, 1, sig ? writech : putch); 473 tputs(attrs_off, 1, sig ? writech : putch); 474 } 475 476 static int 477 colortype(mode_t mode) 478 { 479 switch (mode & S_IFMT) { 480 case S_IFDIR: 481 if (mode & S_IWOTH) 482 if (mode & S_ISTXT) 483 printcolor(C_WSDIR); 484 else 485 printcolor(C_WDIR); 486 else 487 printcolor(C_DIR); 488 return (1); 489 case S_IFLNK: 490 printcolor(C_LNK); 491 return (1); 492 case S_IFSOCK: 493 printcolor(C_SOCK); 494 return (1); 495 case S_IFIFO: 496 printcolor(C_FIFO); 497 return (1); 498 case S_IFBLK: 499 printcolor(C_BLK); 500 return (1); 501 case S_IFCHR: 502 printcolor(C_CHR); 503 return (1); 504 default:; 505 } 506 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 507 if (mode & S_ISUID) 508 printcolor(C_SUID); 509 else if (mode & S_ISGID) 510 printcolor(C_SGID); 511 else 512 printcolor(C_EXEC); 513 return (1); 514 } 515 return (0); 516 } 517 518 void 519 parsecolors(const char *cs) 520 { 521 int i; 522 int j; 523 size_t len; 524 char c[2]; 525 short legacy_warn = 0; 526 527 if (cs == NULL) 528 cs = ""; /* LSCOLORS not set */ 529 len = strlen(cs); 530 for (i = 0; i < C_NUMCOLORS; i++) { 531 colors[i].bold = 0; 532 533 if (len <= 2 * (size_t)i) { 534 c[0] = defcolors[2 * i]; 535 c[1] = defcolors[2 * i + 1]; 536 } else { 537 c[0] = cs[2 * i]; 538 c[1] = cs[2 * i + 1]; 539 } 540 for (j = 0; j < 2; j++) { 541 /* Legacy colours used 0-7 */ 542 if (c[j] >= '0' && c[j] <= '7') { 543 colors[i].num[j] = c[j] - '0'; 544 if (!legacy_warn) { 545 warnx("LSCOLORS should use " 546 "characters a-h instead of 0-9 (" 547 "see the manual page)\n"); 548 } 549 legacy_warn = 1; 550 } else if (c[j] >= 'a' && c[j] <= 'h') 551 colors[i].num[j] = c[j] - 'a'; 552 else if (c[j] >= 'A' && c[j] <= 'H') { 553 colors[i].num[j] = c[j] - 'A'; 554 colors[i].bold = 1; 555 } else if (tolower((unsigned char)c[j] == 'x')) 556 colors[i].num[j] = -1; 557 else { 558 warnx("invalid character '%c' in LSCOLORS" 559 " env var\n", c[j]); 560 colors[i].num[j] = -1; 561 } 562 } 563 } 564 } 565 566 void 567 colorquit(int sig) 568 { 569 endcolor(sig); 570 571 signal(sig, SIG_DFL); 572 kill(getpid(), sig); 573 } 574 575 #endif /* COLORLS */ 576 577 static void 578 printlink(const FTSENT *p) 579 { 580 int lnklen; 581 char name[MAXPATHLEN + 1]; 582 char path[MAXPATHLEN + 1]; 583 584 if (p->fts_level == FTS_ROOTLEVEL) 585 snprintf(name, sizeof(name), "%s", p->fts_name); 586 else 587 snprintf(name, sizeof(name), 588 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 589 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 590 fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); 591 return; 592 } 593 path[lnklen] = '\0'; 594 printf(" -> "); 595 printname(path); 596 } 597 598 static void 599 printsize(size_t width, off_t bytes) 600 { 601 unit_t unit; 602 603 if (f_humanval) { 604 unit = unit_adjust(&bytes); 605 606 if (bytes == 0) 607 printf("%*s ", width, "0B"); 608 else 609 printf("%*lld%c ", width - 1, bytes, 610 "BKMGTPE"[unit]); 611 } else 612 printf("%*lld ", width, bytes); 613 } 614 615 /* 616 * Output in "human-readable" format. Uses 3 digits max and puts 617 * unit suffixes at the end. Makes output compact and easy to read, 618 * especially on huge disks. 619 * 620 */ 621 static unit_t 622 unit_adjust(off_t *val) 623 { 624 double abval; 625 unit_t unit; 626 u_int unit_sz; 627 628 abval = fabs((double)*val); 629 630 unit_sz = abval ? (u_int)ilogb(abval) / 10 : 0; 631 632 if (unit_sz >= (u_int)UNIT_MAX) { 633 unit = NONE; 634 } else { 635 unit = unitp[unit_sz]; 636 *val /= (double)vals_base2[unit_sz]; 637 } 638 639 return (unit); 640 } 641