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