1 /* $NetBSD: print.c,v 1.55 2014/05/10 09:39:18 martin Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Michael Fischbein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94"; 39 #else 40 __RCSID("$NetBSD: print.c,v 1.55 2014/05/10 09:39:18 martin Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/stat.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <inttypes.h> 50 #include <fts.h> 51 #include <grp.h> 52 #include <pwd.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <time.h> 57 #include <tzfile.h> 58 #include <unistd.h> 59 #include <util.h> 60 61 #include "ls.h" 62 #include "extern.h" 63 64 extern int termwidth; 65 66 static int printaname(FTSENT *, int, int); 67 static void printlink(FTSENT *); 68 static void printtime(time_t); 69 static void printtotal(DISPLAY *dp); 70 static int printtype(u_int); 71 72 static time_t now; 73 74 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 75 76 static int 77 safe_printpath(const FTSENT *p) { 78 int chcnt; 79 80 if (f_fullpath) { 81 chcnt = safe_print(p->fts_path); 82 chcnt += safe_print("/"); 83 } else 84 chcnt = 0; 85 return chcnt + safe_print(p->fts_name); 86 } 87 88 static int 89 printescapedpath(const FTSENT *p) { 90 int chcnt; 91 92 if (f_fullpath) { 93 chcnt = printescaped(p->fts_path); 94 chcnt += printescaped("/"); 95 } else 96 chcnt = 0; 97 98 return chcnt + printescaped(p->fts_name); 99 } 100 101 static int 102 printpath(const FTSENT *p) { 103 if (f_fullpath) 104 return printf("%s/%s", p->fts_path, p->fts_name); 105 else 106 return printf("%s", p->fts_name); 107 } 108 109 void 110 printscol(DISPLAY *dp) 111 { 112 FTSENT *p; 113 114 for (p = dp->list; p; p = p->fts_link) { 115 if (IS_NOPRINT(p)) 116 continue; 117 (void)printaname(p, dp->s_inode, dp->s_block); 118 (void)putchar('\n'); 119 } 120 } 121 122 void 123 printlong(DISPLAY *dp) 124 { 125 struct stat *sp; 126 FTSENT *p; 127 NAMES *np; 128 char buf[20], szbuf[5]; 129 130 now = time(NULL); 131 132 if (!f_leafonly) 133 printtotal(dp); /* "total: %u\n" */ 134 135 for (p = dp->list; p; p = p->fts_link) { 136 if (IS_NOPRINT(p)) 137 continue; 138 sp = p->fts_statp; 139 if (f_inode) 140 (void)printf("%*"PRIu64" ", dp->s_inode, sp->st_ino); 141 if (f_size) { 142 if (f_humanize) { 143 if ((humanize_number(szbuf, sizeof(szbuf), 144 sp->st_blocks * S_BLKSIZE, 145 "", HN_AUTOSCALE, 146 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 147 err(1, "humanize_number"); 148 (void)printf("%*s ", dp->s_block, szbuf); 149 } else { 150 (void)printf(f_commas ? "%'*llu " : "%*llu ", 151 dp->s_block, 152 (unsigned long long)howmany(sp->st_blocks, 153 blocksize)); 154 } 155 } 156 (void)strmode(sp->st_mode, buf); 157 np = p->fts_pointer; 158 (void)printf("%s %*lu ", buf, dp->s_nlink, 159 (unsigned long)sp->st_nlink); 160 if (!f_grouponly) 161 (void)printf("%-*s ", dp->s_user, np->user); 162 (void)printf("%-*s ", dp->s_group, np->group); 163 if (f_flags) 164 (void)printf("%-*s ", dp->s_flags, np->flags); 165 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 166 (void)printf("%*lld, %*lld ", 167 dp->s_major, (long long)major(sp->st_rdev), 168 dp->s_minor, (long long)minor(sp->st_rdev)); 169 else 170 if (f_humanize) { 171 if ((humanize_number(szbuf, sizeof(szbuf), 172 sp->st_size, "", HN_AUTOSCALE, 173 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 174 err(1, "humanize_number"); 175 (void)printf("%*s ", dp->s_size, szbuf); 176 } else { 177 (void)printf(f_commas ? "%'*llu " : "%*llu ", 178 dp->s_size, (unsigned long long) 179 sp->st_size); 180 } 181 if (f_accesstime) 182 printtime(sp->st_atime); 183 else if (f_statustime) 184 printtime(sp->st_ctime); 185 else 186 printtime(sp->st_mtime); 187 if (f_octal || f_octal_escape) 188 (void)safe_printpath(p); 189 else if (f_nonprint) 190 (void)printescapedpath(p); 191 else 192 (void)printpath(p); 193 194 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 195 (void)printtype(sp->st_mode); 196 if (S_ISLNK(sp->st_mode)) 197 printlink(p); 198 (void)putchar('\n'); 199 } 200 } 201 202 void 203 printcol(DISPLAY *dp) 204 { 205 static FTSENT **array; 206 static int lastentries = -1; 207 FTSENT *p; 208 int base, chcnt, col, colwidth, num; 209 int numcols, numrows, row; 210 211 colwidth = dp->maxlen; 212 if (f_inode) 213 colwidth += dp->s_inode + 1; 214 if (f_size) { 215 if (f_humanize) 216 colwidth += dp->s_size + 1; 217 else 218 colwidth += dp->s_block + 1; 219 } 220 if (f_type || f_typedir) 221 colwidth += 1; 222 223 colwidth += 1; 224 225 if (termwidth < 2 * colwidth) { 226 printscol(dp); 227 return; 228 } 229 230 /* 231 * Have to do random access in the linked list -- build a table 232 * of pointers. 233 */ 234 if (dp->entries > lastentries) { 235 FTSENT **newarray; 236 237 newarray = realloc(array, dp->entries * sizeof(FTSENT *)); 238 if (newarray == NULL) { 239 warn(NULL); 240 printscol(dp); 241 return; 242 } 243 lastentries = dp->entries; 244 array = newarray; 245 } 246 for (p = dp->list, num = 0; p; p = p->fts_link) 247 if (p->fts_number != NO_PRINT) 248 array[num++] = p; 249 250 numcols = termwidth / colwidth; 251 colwidth = termwidth / numcols; /* spread out if possible */ 252 numrows = num / numcols; 253 if (num % numcols) 254 ++numrows; 255 256 printtotal(dp); /* "total: %u\n" */ 257 258 for (row = 0; row < numrows; ++row) { 259 for (base = row, chcnt = col = 0; col < numcols; ++col) { 260 chcnt = printaname(array[base], dp->s_inode, 261 f_humanize ? dp->s_size : dp->s_block); 262 if ((base += numrows) >= num) 263 break; 264 while (chcnt++ < colwidth) 265 (void)putchar(' '); 266 } 267 (void)putchar('\n'); 268 } 269 } 270 271 void 272 printacol(DISPLAY *dp) 273 { 274 FTSENT *p; 275 int chcnt, col, colwidth; 276 int numcols; 277 278 colwidth = dp->maxlen; 279 if (f_inode) 280 colwidth += dp->s_inode + 1; 281 if (f_size) { 282 if (f_humanize) 283 colwidth += dp->s_size + 1; 284 else 285 colwidth += dp->s_block + 1; 286 } 287 if (f_type || f_typedir) 288 colwidth += 1; 289 290 colwidth += 1; 291 292 if (termwidth < 2 * colwidth) { 293 printscol(dp); 294 return; 295 } 296 297 numcols = termwidth / colwidth; 298 colwidth = termwidth / numcols; /* spread out if possible */ 299 300 printtotal(dp); /* "total: %u\n" */ 301 302 chcnt = col = 0; 303 for (p = dp->list; p; p = p->fts_link) { 304 if (IS_NOPRINT(p)) 305 continue; 306 if (col >= numcols) { 307 chcnt = col = 0; 308 (void)putchar('\n'); 309 } 310 chcnt = printaname(p, dp->s_inode, 311 f_humanize ? dp->s_size : dp->s_block); 312 while (chcnt++ < colwidth) 313 (void)putchar(' '); 314 col++; 315 } 316 (void)putchar('\n'); 317 } 318 319 void 320 printstream(DISPLAY *dp) 321 { 322 FTSENT *p; 323 int col; 324 int extwidth; 325 326 extwidth = 0; 327 if (f_inode) 328 extwidth += dp->s_inode + 1; 329 if (f_size) { 330 if (f_humanize) 331 extwidth += dp->s_size + 1; 332 else 333 extwidth += dp->s_block + 1; 334 } 335 if (f_type) 336 extwidth += 1; 337 338 for (col = 0, p = dp->list; p != NULL; p = p->fts_link) { 339 if (IS_NOPRINT(p)) 340 continue; 341 if (col > 0) { 342 (void)putchar(','), col++; 343 if (col + 1 + extwidth + (int)p->fts_namelen >= termwidth) 344 (void)putchar('\n'), col = 0; 345 else 346 (void)putchar(' '), col++; 347 } 348 col += printaname(p, dp->s_inode, 349 f_humanize ? dp->s_size : dp->s_block); 350 } 351 (void)putchar('\n'); 352 } 353 354 /* 355 * print [inode] [size] name 356 * return # of characters printed, no trailing characters. 357 */ 358 static int 359 printaname(FTSENT *p, int inodefield, int sizefield) 360 { 361 struct stat *sp; 362 int chcnt; 363 char szbuf[5]; 364 365 sp = p->fts_statp; 366 chcnt = 0; 367 if (f_inode) 368 chcnt += printf("%*"PRIu64" ", inodefield, sp->st_ino); 369 if (f_size) { 370 if (f_humanize) { 371 if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size, 372 "", HN_AUTOSCALE, 373 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 374 err(1, "humanize_number"); 375 chcnt += printf("%*s ", sizefield, szbuf); 376 } else { 377 chcnt += printf(f_commas ? "%'*llu " : "%*llu ", 378 sizefield, (unsigned long long) 379 howmany(sp->st_blocks, blocksize)); 380 } 381 } 382 if (f_octal || f_octal_escape) 383 chcnt += safe_printpath(p); 384 else if (f_nonprint) 385 chcnt += printescapedpath(p); 386 else 387 chcnt += printpath(p); 388 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 389 chcnt += printtype(sp->st_mode); 390 return (chcnt); 391 } 392 393 static void 394 printtime(time_t ftime) 395 { 396 int i; 397 const char *longstring; 398 399 if ((longstring = ctime(&ftime)) == NULL) { 400 /* 012345678901234567890123 */ 401 longstring = "????????????????????????"; 402 } 403 for (i = 4; i < 11; ++i) 404 (void)putchar(longstring[i]); 405 406 #define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) 407 if (f_sectime) 408 for (i = 11; i < 24; i++) 409 (void)putchar(longstring[i]); 410 else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now) 411 for (i = 11; i < 16; ++i) 412 (void)putchar(longstring[i]); 413 else { 414 (void)putchar(' '); 415 for (i = 20; i < 24; ++i) 416 (void)putchar(longstring[i]); 417 } 418 (void)putchar(' '); 419 } 420 421 /* 422 * Display total used disk space in the form "total: %u\n". 423 * Note: POSIX (IEEE Std 1003.1-2001) says this should be always in 512 blocks, 424 * but we humanise it with -h, or separate it with commas with -M, and use 1024 425 * with -k. 426 */ 427 static void 428 printtotal(DISPLAY *dp) 429 { 430 char szbuf[5]; 431 432 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { 433 if (f_humanize) { 434 if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal, 435 "", HN_AUTOSCALE, 436 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 437 err(1, "humanize_number"); 438 (void)printf("total %s\n", szbuf); 439 } else { 440 (void)printf(f_commas ? "total %'llu\n" : 441 "total %llu\n", (unsigned long long) 442 howmany(dp->btotal, blocksize)); 443 } 444 } 445 } 446 447 static int 448 printtype(u_int mode) 449 { 450 switch (mode & S_IFMT) { 451 case S_IFDIR: 452 (void)putchar('/'); 453 return (1); 454 case S_IFIFO: 455 (void)putchar('|'); 456 return (1); 457 case S_IFLNK: 458 (void)putchar('@'); 459 return (1); 460 case S_IFSOCK: 461 (void)putchar('='); 462 return (1); 463 case S_IFWHT: 464 (void)putchar('%'); 465 return (1); 466 } 467 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 468 (void)putchar('*'); 469 return (1); 470 } 471 return (0); 472 } 473 474 static void 475 printlink(FTSENT *p) 476 { 477 int lnklen; 478 char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; 479 480 if (p->fts_level == FTS_ROOTLEVEL) 481 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 482 else 483 (void)snprintf(name, sizeof(name), 484 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 485 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 486 (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); 487 return; 488 } 489 path[lnklen] = '\0'; 490 (void)printf(" -> "); 491 if (f_octal || f_octal_escape) 492 (void)safe_print(path); 493 else if (f_nonprint) 494 (void)printescaped(path); 495 else 496 (void)printf("%s", path); 497 } 498