1/* $NetBSD: print.c,v 1.49 2011/03/15 03:52:38 erh 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 38static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94"; 39#else 40__RCSID("$NetBSD: print.c,v 1.49 2011/03/15 03:52:38 erh 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 <fts.h> 50#include <grp.h> 51#include <pwd.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <time.h> 56#include <tzfile.h> 57#include <unistd.h> 58#include <util.h> 59 60#include "ls.h" 61#include "extern.h" 62 63extern int termwidth; 64 65static int printaname(FTSENT *, int, int); 66static void printlink(FTSENT *); 67static void printtime(time_t); 68static void printtotal(DISPLAY *dp); 69static int printtype(u_int); 70 71static time_t now; 72 73#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 74 75void 76printscol(DISPLAY *dp) 77{ 78 FTSENT *p; 79 80 for (p = dp->list; p; p = p->fts_link) { 81 if (IS_NOPRINT(p)) 82 continue; 83 (void)printaname(p, dp->s_inode, dp->s_block); 84 (void)putchar('\n'); 85 } 86} 87 88void 89printlong(DISPLAY *dp) 90{ 91 struct stat *sp; 92 FTSENT *p; 93 NAMES *np; 94 char buf[20], szbuf[5]; 95 96 now = time(NULL); 97 98 printtotal(dp); /* "total: %u\n" */ 99 100 for (p = dp->list; p; p = p->fts_link) { 101 if (IS_NOPRINT(p)) 102 continue; 103 sp = p->fts_statp; 104 if (f_inode) 105 (void)printf("%*lu ", dp->s_inode, 106 (unsigned long)sp->st_ino); 107 if (f_size) { 108 if (f_humanize) { 109 if ((humanize_number(szbuf, sizeof(szbuf), 110 sp->st_blocks * S_BLKSIZE, 111 "", HN_AUTOSCALE, 112 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 113 err(1, "humanize_number"); 114 (void)printf("%*s ", dp->s_block, szbuf); 115 } else { 116 (void)printf(f_commas ? "%'*llu " : "%*llu ", 117 dp->s_block, 118 (unsigned long long)howmany(sp->st_blocks, 119 blocksize)); 120 } 121 } 122 (void)strmode(sp->st_mode, buf); 123 np = p->fts_pointer; 124 (void)printf("%s %*lu ", buf, dp->s_nlink, 125 (unsigned long)sp->st_nlink); 126 if (!f_grouponly) 127 (void)printf("%-*s ", dp->s_user, np->user); 128 (void)printf("%-*s ", dp->s_group, np->group); 129 if (f_flags) 130 (void)printf("%-*s ", dp->s_flags, np->flags); 131 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 132 (void)printf("%*lld, %*lld ", 133 dp->s_major, (long long)major(sp->st_rdev), 134 dp->s_minor, (long long)minor(sp->st_rdev)); 135 else 136 if (f_humanize) { 137 if ((humanize_number(szbuf, sizeof(szbuf), 138 sp->st_size, "", HN_AUTOSCALE, 139 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 140 err(1, "humanize_number"); 141 (void)printf("%*s ", dp->s_size, szbuf); 142 } else { 143 (void)printf(f_commas ? "%'*llu " : "%*llu ", 144 dp->s_size, (unsigned long long) 145 sp->st_size); 146 } 147 if (f_accesstime) 148 printtime(sp->st_atime); 149 else if (f_statustime) 150 printtime(sp->st_ctime); 151 else 152 printtime(sp->st_mtime); 153 if (f_octal || f_octal_escape) 154 (void)safe_print(p->fts_name); 155 else if (f_nonprint) 156 (void)printescaped(p->fts_name); 157 else 158 (void)printf("%s", p->fts_name); 159 160 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 161 (void)printtype(sp->st_mode); 162 if (S_ISLNK(sp->st_mode)) 163 printlink(p); 164 (void)putchar('\n'); 165 } 166} 167 168void 169printcol(DISPLAY *dp) 170{ 171 static FTSENT **array; 172 static int lastentries = -1; 173 FTSENT *p; 174 int base, chcnt, col, colwidth, num; 175 int numcols, numrows, row; 176 177 colwidth = dp->maxlen; 178 if (f_inode) 179 colwidth += dp->s_inode + 1; 180 if (f_size) { 181 if (f_humanize) 182 colwidth += dp->s_size + 1; 183 else 184 colwidth += dp->s_block + 1; 185 } 186 if (f_type || f_typedir) 187 colwidth += 1; 188 189 colwidth += 1; 190 191 if (termwidth < 2 * colwidth) { 192 printscol(dp); 193 return; 194 } 195 196 /* 197 * Have to do random access in the linked list -- build a table 198 * of pointers. 199 */ 200 if (dp->entries > lastentries) { 201 lastentries = dp->entries; 202 if ((array = 203 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 204 warn(NULL); 205 printscol(dp); 206 } 207 } 208 for (p = dp->list, num = 0; p; p = p->fts_link) 209 if (p->fts_number != NO_PRINT) 210 array[num++] = p; 211 212 numcols = termwidth / colwidth; 213 colwidth = termwidth / numcols; /* spread out if possible */ 214 numrows = num / numcols; 215 if (num % numcols) 216 ++numrows; 217 218 printtotal(dp); /* "total: %u\n" */ 219 220 for (row = 0; row < numrows; ++row) { 221 for (base = row, chcnt = col = 0; col < numcols; ++col) { 222 chcnt = printaname(array[base], dp->s_inode, 223 f_humanize ? dp->s_size : dp->s_block); 224 if ((base += numrows) >= num) 225 break; 226 while (chcnt++ < colwidth) 227 (void)putchar(' '); 228 } 229 (void)putchar('\n'); 230 } 231} 232 233void 234printacol(DISPLAY *dp) 235{ 236 FTSENT *p; 237 int chcnt, col, colwidth; 238 int numcols; 239 240 colwidth = dp->maxlen; 241 if (f_inode) 242 colwidth += dp->s_inode + 1; 243 if (f_size) { 244 if (f_humanize) 245 colwidth += dp->s_size + 1; 246 else 247 colwidth += dp->s_block + 1; 248 } 249 if (f_type || f_typedir) 250 colwidth += 1; 251 252 colwidth += 1; 253 254 if (termwidth < 2 * colwidth) { 255 printscol(dp); 256 return; 257 } 258 259 numcols = termwidth / colwidth; 260 colwidth = termwidth / numcols; /* spread out if possible */ 261 262 printtotal(dp); /* "total: %u\n" */ 263 264 chcnt = col = 0; 265 for (p = dp->list; p; p = p->fts_link) { 266 if (IS_NOPRINT(p)) 267 continue; 268 if (col >= numcols) { 269 chcnt = col = 0; 270 (void)putchar('\n'); 271 } 272 chcnt = printaname(p, dp->s_inode, 273 f_humanize ? dp->s_size : dp->s_block); 274 while (chcnt++ < colwidth) 275 (void)putchar(' '); 276 col++; 277 } 278 (void)putchar('\n'); 279} 280 281void 282printstream(DISPLAY *dp) 283{ 284 FTSENT *p; 285 int col; 286 int extwidth; 287 288 extwidth = 0; 289 if (f_inode) 290 extwidth += dp->s_inode + 1; 291 if (f_size) { 292 if (f_humanize) 293 extwidth += dp->s_size + 1; 294 else 295 extwidth += dp->s_block + 1; 296 } 297 if (f_type) 298 extwidth += 1; 299 300 for (col = 0, p = dp->list; p != NULL; p = p->fts_link) { 301 if (IS_NOPRINT(p)) 302 continue; 303 if (col > 0) { 304 (void)putchar(','), col++; 305 if (col + 1 + extwidth + (int)p->fts_namelen >= termwidth) 306 (void)putchar('\n'), col = 0; 307 else 308 (void)putchar(' '), col++; 309 } 310 col += printaname(p, dp->s_inode, 311 f_humanize ? dp->s_size : dp->s_block); 312 } 313 (void)putchar('\n'); 314} 315 316/* 317 * print [inode] [size] name 318 * return # of characters printed, no trailing characters. 319 */ 320static int 321printaname(FTSENT *p, int inodefield, int sizefield) 322{ 323 struct stat *sp; 324 int chcnt; 325 char szbuf[5]; 326 327 sp = p->fts_statp; 328 chcnt = 0; 329 if (f_inode) 330 chcnt += printf("%*lu ", inodefield, (unsigned long)sp->st_ino); 331 if (f_size) { 332 if (f_humanize) { 333 if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size, 334 "", HN_AUTOSCALE, 335 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 336 err(1, "humanize_number"); 337 chcnt += printf("%*s ", sizefield, szbuf); 338 } else { 339 chcnt += printf(f_commas ? "%'*llu " : "%*llu ", 340 sizefield, (unsigned long long) 341 howmany(sp->st_blocks, blocksize)); 342 } 343 } 344 if (f_octal || f_octal_escape) 345 chcnt += safe_print(p->fts_name); 346 else if (f_nonprint) 347 chcnt += printescaped(p->fts_name); 348 else 349 chcnt += printf("%s", p->fts_name); 350 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 351 chcnt += printtype(sp->st_mode); 352 return (chcnt); 353} 354 355static void 356printtime(time_t ftime) 357{ 358 int i; 359 const char *longstring; 360 361 if ((longstring = ctime(&ftime)) == NULL) { 362 /* 012345678901234567890123 */ 363 longstring = "????????????????????????"; 364 } 365 for (i = 4; i < 11; ++i) 366 (void)putchar(longstring[i]); 367 368#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) 369 if (f_sectime) 370 for (i = 11; i < 24; i++) 371 (void)putchar(longstring[i]); 372 else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now) 373 for (i = 11; i < 16; ++i) 374 (void)putchar(longstring[i]); 375 else { 376 (void)putchar(' '); 377 for (i = 20; i < 24; ++i) 378 (void)putchar(longstring[i]); 379 } 380 (void)putchar(' '); 381} 382 383/* 384 * Display total used disk space in the form "total: %u\n". 385 * Note: POSIX (IEEE Std 1003.1-2001) says this should be always in 512 blocks, 386 * but we humanise it with -h, or separate it with commas with -M, and use 1024 387 * with -k. 388 */ 389static void 390printtotal(DISPLAY *dp) 391{ 392 char szbuf[5]; 393 394 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { 395 if (f_humanize) { 396 if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal, 397 "", HN_AUTOSCALE, 398 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 399 err(1, "humanize_number"); 400 (void)printf("total %s\n", szbuf); 401 } else { 402 (void)printf(f_commas ? "total %'llu\n" : 403 "total %llu\n", (unsigned long long) 404 howmany(dp->btotal, blocksize)); 405 } 406 } 407} 408 409static int 410printtype(u_int mode) 411{ 412 switch (mode & S_IFMT) { 413 case S_IFDIR: 414 (void)putchar('/'); 415 return (1); 416 case S_IFIFO: 417 (void)putchar('|'); 418 return (1); 419 case S_IFLNK: 420 (void)putchar('@'); 421 return (1); 422 case S_IFSOCK: 423 (void)putchar('='); 424 return (1); 425 case S_IFWHT: 426 (void)putchar('%'); 427 return (1); 428 } 429 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 430 (void)putchar('*'); 431 return (1); 432 } 433 return (0); 434} 435 436static void 437printlink(FTSENT *p) 438{ 439 int lnklen; 440 char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; 441 442 if (p->fts_level == FTS_ROOTLEVEL) 443 (void)snprintf(name, sizeof(name), "%s", p->fts_name); 444 else 445 (void)snprintf(name, sizeof(name), 446 "%s/%s", p->fts_parent->fts_accpath, p->fts_name); 447 if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { 448 (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); 449 return; 450 } 451 path[lnklen] = '\0'; 452 (void)printf(" -> "); 453 if (f_octal || f_octal_escape) 454 (void)safe_print(path); 455 else if (f_nonprint) 456 (void)printescaped(path); 457 else 458 (void)printf("%s", path); 459} 460 461