1 /* $NetBSD: print.c,v 1.42 2006/12/14 14:15:26 christos 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.42 2006/12/14 14:15:26 christos 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 63 extern int termwidth; 64 65 static int printaname(FTSENT *, int, int); 66 static void printlink(FTSENT *); 67 static void printtime(time_t); 68 static int printtype(u_int); 69 70 static time_t now; 71 72 #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) 73 74 void 75 printscol(DISPLAY *dp) 76 { 77 FTSENT *p; 78 79 for (p = dp->list; p; p = p->fts_link) { 80 if (IS_NOPRINT(p)) 81 continue; 82 (void)printaname(p, dp->s_inode, dp->s_block); 83 (void)putchar('\n'); 84 } 85 } 86 87 void 88 printlong(DISPLAY *dp) 89 { 90 struct stat *sp; 91 FTSENT *p; 92 NAMES *np; 93 char buf[20], szbuf[5]; 94 95 now = time(NULL); 96 97 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { 98 if (f_humanize) { 99 if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal, 100 "", HN_AUTOSCALE, 101 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 102 err(1, "humanize_number"); 103 (void)printf("total %s\n", szbuf); 104 } else { 105 (void)printf("total %llu\n", 106 (long long)(howmany(dp->btotal, blocksize))); 107 } 108 } 109 110 for (p = dp->list; p; p = p->fts_link) { 111 if (IS_NOPRINT(p)) 112 continue; 113 sp = p->fts_statp; 114 if (f_inode) 115 (void)printf("%*lu ", dp->s_inode, 116 (unsigned long)sp->st_ino); 117 if (f_size) { 118 if (f_humanize) { 119 if ((humanize_number(szbuf, sizeof(szbuf), 120 sp->st_blocks * S_BLKSIZE, 121 "", HN_AUTOSCALE, 122 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 123 err(1, "humanize_number"); 124 (void)printf("%*s ", dp->s_block, szbuf); 125 } else { 126 (void)printf("%*llu ", dp->s_block, 127 (long long)howmany(sp->st_blocks, 128 blocksize)); 129 } 130 } 131 (void)strmode(sp->st_mode, buf); 132 np = p->fts_pointer; 133 (void)printf("%s %*lu ", buf, dp->s_nlink, 134 (unsigned long)sp->st_nlink); 135 if (!f_grouponly) 136 (void)printf("%-*s ", dp->s_user, np->user); 137 (void)printf("%-*s ", dp->s_group, np->group); 138 if (f_flags) 139 (void)printf("%-*s ", dp->s_flags, np->flags); 140 if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) 141 (void)printf("%*u, %*u ", 142 dp->s_major, major(sp->st_rdev), dp->s_minor, 143 minor(sp->st_rdev)); 144 else 145 if (f_humanize) { 146 if ((humanize_number(szbuf, sizeof(szbuf), 147 sp->st_size, "", HN_AUTOSCALE, 148 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 149 err(1, "humanize_number"); 150 (void)printf("%*s ", dp->s_size, szbuf); 151 } else { 152 (void)printf("%*llu ", dp->s_size, 153 (long long)sp->st_size); 154 } 155 if (f_accesstime) 156 printtime(sp->st_atime); 157 else if (f_statustime) 158 printtime(sp->st_ctime); 159 else 160 printtime(sp->st_mtime); 161 if (f_octal || f_octal_escape) 162 (void)safe_print(p->fts_name); 163 else if (f_nonprint) 164 (void)printescaped(p->fts_name); 165 else 166 (void)printf("%s", p->fts_name); 167 168 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 169 (void)printtype(sp->st_mode); 170 if (S_ISLNK(sp->st_mode)) 171 printlink(p); 172 (void)putchar('\n'); 173 } 174 } 175 176 void 177 printcol(DISPLAY *dp) 178 { 179 static FTSENT **array; 180 static int lastentries = -1; 181 FTSENT *p; 182 int base, chcnt, col, colwidth, num; 183 int numcols, numrows, row; 184 char szbuf[5]; 185 186 colwidth = dp->maxlen; 187 if (f_inode) 188 colwidth += dp->s_inode + 1; 189 if (f_size) { 190 if (f_humanize) 191 colwidth += dp->s_size + 1; 192 else 193 colwidth += dp->s_block + 1; 194 } 195 if (f_type || f_typedir) 196 colwidth += 1; 197 198 colwidth += 1; 199 200 if (termwidth < 2 * colwidth) { 201 printscol(dp); 202 return; 203 } 204 205 /* 206 * Have to do random access in the linked list -- build a table 207 * of pointers. 208 */ 209 if (dp->entries > lastentries) { 210 lastentries = dp->entries; 211 if ((array = 212 realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { 213 warn(NULL); 214 printscol(dp); 215 } 216 } 217 for (p = dp->list, num = 0; p; p = p->fts_link) 218 if (p->fts_number != NO_PRINT) 219 array[num++] = p; 220 221 numcols = termwidth / colwidth; 222 colwidth = termwidth / numcols; /* spread out if possible */ 223 numrows = num / numcols; 224 if (num % numcols) 225 ++numrows; 226 227 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { 228 if (f_humanize) { 229 if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal, 230 "", HN_AUTOSCALE, 231 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 232 err(1, "humanize_number"); 233 (void)printf("total %s\n", szbuf); 234 } else { 235 (void)printf("total %llu\n", 236 (long long)(howmany(dp->btotal, blocksize))); 237 } 238 } 239 for (row = 0; row < numrows; ++row) { 240 for (base = row, chcnt = col = 0; col < numcols; ++col) { 241 chcnt = printaname(array[base], dp->s_inode, 242 f_humanize ? dp->s_size : dp->s_block); 243 if ((base += numrows) >= num) 244 break; 245 while (chcnt++ < colwidth) 246 (void)putchar(' '); 247 } 248 (void)putchar('\n'); 249 } 250 } 251 252 void 253 printacol(DISPLAY *dp) 254 { 255 FTSENT *p; 256 int chcnt, col, colwidth; 257 int numcols; 258 char szbuf[5]; 259 260 colwidth = dp->maxlen; 261 if (f_inode) 262 colwidth += dp->s_inode + 1; 263 if (f_size) { 264 if (f_humanize) 265 colwidth += dp->s_size + 1; 266 else 267 colwidth += dp->s_block + 1; 268 } 269 if (f_type || f_typedir) 270 colwidth += 1; 271 272 colwidth += 1; 273 274 if (termwidth < 2 * colwidth) { 275 printscol(dp); 276 return; 277 } 278 279 numcols = termwidth / colwidth; 280 colwidth = termwidth / numcols; /* spread out if possible */ 281 282 if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) { 283 if (f_humanize) { 284 if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal, 285 "", HN_AUTOSCALE, 286 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 287 err(1, "humanize_number"); 288 (void)printf("total %s\n", szbuf); 289 } else { 290 (void)printf("total %llu\n", 291 (long long)(howmany(dp->btotal, blocksize))); 292 } 293 } 294 chcnt = col = 0; 295 for (p = dp->list; p; p = p->fts_link) { 296 if (IS_NOPRINT(p)) 297 continue; 298 if (col >= numcols) { 299 chcnt = col = 0; 300 (void)putchar('\n'); 301 } 302 chcnt = printaname(p, dp->s_inode, 303 f_humanize ? dp->s_size : dp->s_block); 304 while (chcnt++ < colwidth) 305 (void)putchar(' '); 306 col++; 307 } 308 (void)putchar('\n'); 309 } 310 311 void 312 printstream(DISPLAY *dp) 313 { 314 FTSENT *p; 315 int col; 316 int extwidth; 317 318 extwidth = 0; 319 if (f_inode) 320 extwidth += dp->s_inode + 1; 321 if (f_size) { 322 if (f_humanize) 323 extwidth += dp->s_size + 1; 324 else 325 extwidth += dp->s_block + 1; 326 } 327 if (f_type) 328 extwidth += 1; 329 330 for (col = 0, p = dp->list; p != NULL; p = p->fts_link) { 331 if (IS_NOPRINT(p)) 332 continue; 333 if (col > 0) { 334 (void)putchar(','), col++; 335 if (col + 1 + extwidth + p->fts_namelen >= termwidth) 336 (void)putchar('\n'), col = 0; 337 else 338 (void)putchar(' '), col++; 339 } 340 col += printaname(p, dp->s_inode, 341 f_humanize ? dp->s_size : dp->s_block); 342 } 343 (void)putchar('\n'); 344 } 345 346 /* 347 * print [inode] [size] name 348 * return # of characters printed, no trailing characters. 349 */ 350 static int 351 printaname(FTSENT *p, int inodefield, int sizefield) 352 { 353 struct stat *sp; 354 int chcnt; 355 char szbuf[5]; 356 357 sp = p->fts_statp; 358 chcnt = 0; 359 if (f_inode) 360 chcnt += printf("%*lu ", inodefield, (unsigned long)sp->st_ino); 361 if (f_size) { 362 if (f_humanize) { 363 if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size, 364 "", HN_AUTOSCALE, 365 (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1) 366 err(1, "humanize_number"); 367 chcnt += printf("%*s ", sizefield, szbuf); 368 } else { 369 chcnt += printf("%*llu ", sizefield, 370 (long long)howmany(sp->st_blocks, blocksize)); 371 } 372 } 373 if (f_octal || f_octal_escape) 374 chcnt += safe_print(p->fts_name); 375 else if (f_nonprint) 376 chcnt += printescaped(p->fts_name); 377 else 378 chcnt += printf("%s", p->fts_name); 379 if (f_type || (f_typedir && S_ISDIR(sp->st_mode))) 380 chcnt += printtype(sp->st_mode); 381 return (chcnt); 382 } 383 384 static void 385 printtime(time_t ftime) 386 { 387 int i; 388 char *longstring; 389 390 longstring = ctime(&ftime); 391 for (i = 4; i < 11; ++i) 392 (void)putchar(longstring[i]); 393 394 #define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) 395 if (f_sectime) 396 for (i = 11; i < 24; i++) 397 (void)putchar(longstring[i]); 398 else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now) 399 for (i = 11; i < 16; ++i) 400 (void)putchar(longstring[i]); 401 else { 402 (void)putchar(' '); 403 for (i = 20; i < 24; ++i) 404 (void)putchar(longstring[i]); 405 } 406 (void)putchar(' '); 407 } 408 409 static int 410 printtype(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 436 static void 437 printlink(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