155682Smarkm/* 2233294Sstas * Copyright (c) 1999 - 2002 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 1755682Smarkm * 3. Neither the name of KTH nor the names of its contributors may be 1855682Smarkm * used to endorse or promote products derived from this software without 1955682Smarkm * specific prior written permission. 2055682Smarkm * 2155682Smarkm * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 2255682Smarkm * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2455682Smarkm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 2555682Smarkm * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2655682Smarkm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2755682Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2855682Smarkm * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2955682Smarkm * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3055682Smarkm * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3155682Smarkm * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 3255682Smarkm 3372445Sassar#ifndef TEST 3455682Smarkm#include "ftpd_locl.h" 3555682Smarkm 36233294SstasRCSID("$Id$"); 3755682Smarkm 3872445Sassar#else 3972445Sassar#include <stdio.h> 4072445Sassar#include <string.h> 4172445Sassar#include <stdlib.h> 4272445Sassar#include <time.h> 4372445Sassar#include <dirent.h> 4472445Sassar#include <sys/stat.h> 4572445Sassar#include <unistd.h> 4672445Sassar#include <pwd.h> 4772445Sassar#include <grp.h> 4872445Sassar#include <errno.h> 4972445Sassar 5072445Sassar#define sec_fprintf2 fprintf 5172445Sassar#define sec_fflush fflush 5290926Snectarstatic void list_files(FILE *out, const char **files, int n_files, int flags); 5390926Snectarstatic int parse_flags(const char *options); 5490926Snectar 5572445Sassarint 5672445Sassarmain(int argc, char **argv) 5772445Sassar{ 5890926Snectar int i = 1; 5990926Snectar int flags; 6090926Snectar if(argc > 1 && argv[1][0] == '-') { 6190926Snectar flags = parse_flags(argv[1]); 6290926Snectar i = 2; 6390926Snectar } else 6490926Snectar flags = parse_flags(NULL); 6590926Snectar 6690926Snectar list_files(stdout, (const char **)argv + i, argc - i, flags); 6772445Sassar return 0; 6872445Sassar} 6972445Sassar#endif 7072445Sassar 7155682Smarkmstruct fileinfo { 7255682Smarkm struct stat st; 7355682Smarkm int inode; 7455682Smarkm int bsize; 7555682Smarkm char mode[11]; 7655682Smarkm int n_link; 7755682Smarkm char *user; 7855682Smarkm char *group; 7955682Smarkm char *size; 8055682Smarkm char *major; 8155682Smarkm char *minor; 8255682Smarkm char *date; 8355682Smarkm char *filename; 8455682Smarkm char *link; 8555682Smarkm}; 8655682Smarkm 8755682Smarkmstatic void 8855682Smarkmfree_fileinfo(struct fileinfo *f) 8955682Smarkm{ 9055682Smarkm free(f->user); 9155682Smarkm free(f->group); 9255682Smarkm free(f->size); 9355682Smarkm free(f->major); 9455682Smarkm free(f->minor); 9555682Smarkm free(f->date); 9655682Smarkm free(f->filename); 9755682Smarkm free(f->link); 9855682Smarkm} 9955682Smarkm 10072445Sassar#define LS_DIRS (1 << 0) 10172445Sassar#define LS_IGNORE_DOT (1 << 1) 10272445Sassar#define LS_SORT_MODE (3 << 2) 10355682Smarkm#define SORT_MODE(f) ((f) & LS_SORT_MODE) 10472445Sassar#define LS_SORT_NAME (1 << 2) 10572445Sassar#define LS_SORT_MTIME (2 << 2) 10672445Sassar#define LS_SORT_SIZE (3 << 2) 10772445Sassar#define LS_SORT_REVERSE (1 << 4) 10855682Smarkm 10972445Sassar#define LS_SIZE (1 << 5) 11072445Sassar#define LS_INODE (1 << 6) 11172445Sassar#define LS_TYPE (1 << 7) 11272445Sassar#define LS_DISP_MODE (3 << 8) 11372445Sassar#define DISP_MODE(f) ((f) & LS_DISP_MODE) 11472445Sassar#define LS_DISP_LONG (1 << 8) 11572445Sassar#define LS_DISP_COLUMN (2 << 8) 11672445Sassar#define LS_DISP_CROSS (3 << 8) 11790926Snectar#define LS_SHOW_ALL (1 << 10) 11890926Snectar#define LS_RECURSIVE (1 << 11) 11990926Snectar#define LS_EXTRA_BLANK (1 << 12) 12090926Snectar#define LS_SHOW_DIRNAME (1 << 13) 12190926Snectar#define LS_DIR_FLAG (1 << 14) /* these files come via list_dir */ 12255682Smarkm 12355682Smarkm#ifndef S_ISTXT 12455682Smarkm#define S_ISTXT S_ISVTX 12555682Smarkm#endif 12655682Smarkm 127102644Snectar#if !defined(_S_IFMT) && defined(S_IFMT) 128102644Snectar#define _S_IFMT S_IFMT 129102644Snectar#endif 130102644Snectar 13155682Smarkm#ifndef S_ISSOCK 13255682Smarkm#define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) 13355682Smarkm#endif 13455682Smarkm 13555682Smarkm#ifndef S_ISLNK 13655682Smarkm#define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) 13755682Smarkm#endif 13855682Smarkm 13990926Snectarstatic size_t 14090926Snectarblock_convert(size_t blocks) 14190926Snectar{ 14290926Snectar#ifdef S_BLKSIZE 14390926Snectar return blocks * S_BLKSIZE / 1024; 14490926Snectar#else 14590926Snectar return blocks * 512 / 1024; 14690926Snectar#endif 14790926Snectar} 14890926Snectar 149178825Sdfrstatic int 15090926Snectarmake_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags) 15155682Smarkm{ 15255682Smarkm char buf[128]; 15372445Sassar int file_type = 0; 15455682Smarkm struct stat *st = &file->st; 155233294Sstas 15655682Smarkm file->inode = st->st_ino; 15790926Snectar file->bsize = block_convert(st->st_blocks); 158233294Sstas 15972445Sassar if(S_ISDIR(st->st_mode)) { 16055682Smarkm file->mode[0] = 'd'; 16172445Sassar file_type = '/'; 16272445Sassar } 16355682Smarkm else if(S_ISCHR(st->st_mode)) 16455682Smarkm file->mode[0] = 'c'; 16555682Smarkm else if(S_ISBLK(st->st_mode)) 16655682Smarkm file->mode[0] = 'b'; 16772445Sassar else if(S_ISREG(st->st_mode)) { 16855682Smarkm file->mode[0] = '-'; 16972445Sassar if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 17072445Sassar file_type = '*'; 17172445Sassar } 17272445Sassar else if(S_ISFIFO(st->st_mode)) { 17355682Smarkm file->mode[0] = 'p'; 17472445Sassar file_type = '|'; 17572445Sassar } 17672445Sassar else if(S_ISLNK(st->st_mode)) { 17755682Smarkm file->mode[0] = 'l'; 17872445Sassar file_type = '@'; 17972445Sassar } 18072445Sassar else if(S_ISSOCK(st->st_mode)) { 18155682Smarkm file->mode[0] = 's'; 18272445Sassar file_type = '='; 18372445Sassar } 18455682Smarkm#ifdef S_ISWHT 18572445Sassar else if(S_ISWHT(st->st_mode)) { 18655682Smarkm file->mode[0] = 'w'; 18772445Sassar file_type = '%'; 18872445Sassar } 18955682Smarkm#endif 190233294Sstas else 19155682Smarkm file->mode[0] = '?'; 19255682Smarkm { 193233294Sstas char *x[] = { "---", "--x", "-w-", "-wx", 19455682Smarkm "r--", "r-x", "rw-", "rwx" }; 19555682Smarkm strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); 19655682Smarkm strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); 19755682Smarkm strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); 19855682Smarkm if((st->st_mode & S_ISUID)) { 19955682Smarkm if((st->st_mode & S_IXUSR)) 20055682Smarkm file->mode[3] = 's'; 20155682Smarkm else 20255682Smarkm file->mode[3] = 'S'; 20355682Smarkm } 20455682Smarkm if((st->st_mode & S_ISGID)) { 20555682Smarkm if((st->st_mode & S_IXGRP)) 20655682Smarkm file->mode[6] = 's'; 20755682Smarkm else 20855682Smarkm file->mode[6] = 'S'; 20955682Smarkm } 21055682Smarkm if((st->st_mode & S_ISTXT)) { 21155682Smarkm if((st->st_mode & S_IXOTH)) 21255682Smarkm file->mode[9] = 't'; 21355682Smarkm else 21455682Smarkm file->mode[9] = 'T'; 21555682Smarkm } 21655682Smarkm } 21755682Smarkm file->n_link = st->st_nlink; 21855682Smarkm { 21955682Smarkm struct passwd *pwd; 22055682Smarkm pwd = getpwuid(st->st_uid); 221178825Sdfr if(pwd == NULL) { 222178825Sdfr if (asprintf(&file->user, "%u", (unsigned)st->st_uid) == -1) 223178825Sdfr file->user = NULL; 224178825Sdfr } else 22555682Smarkm file->user = strdup(pwd->pw_name); 226178825Sdfr if (file->user == NULL) { 227178825Sdfr syslog(LOG_ERR, "out of memory"); 228178825Sdfr return -1; 229178825Sdfr } 23055682Smarkm } 23155682Smarkm { 23255682Smarkm struct group *grp; 23355682Smarkm grp = getgrgid(st->st_gid); 234178825Sdfr if(grp == NULL) { 235178825Sdfr if (asprintf(&file->group, "%u", (unsigned)st->st_gid) == -1) 236178825Sdfr file->group = NULL; 237178825Sdfr } else 23855682Smarkm file->group = strdup(grp->gr_name); 239178825Sdfr if (file->group == NULL) { 240178825Sdfr syslog(LOG_ERR, "out of memory"); 241178825Sdfr return -1; 242178825Sdfr } 24355682Smarkm } 244233294Sstas 24555682Smarkm if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 24655682Smarkm#if defined(major) && defined(minor) 247178825Sdfr if (asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)) == -1) 248178825Sdfr file->major = NULL; 249178825Sdfr if (asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)) == -1) 250178825Sdfr file->minor = NULL; 25155682Smarkm#else 25255682Smarkm /* Don't want to use the DDI/DKI crap. */ 253178825Sdfr if (asprintf(&file->major, "%u", (unsigned)st->st_rdev) == -1) 254178825Sdfr file->major = NULL; 255178825Sdfr if (asprintf(&file->minor, "%u", 0) == -1) 256178825Sdfr file->minor = NULL; 25755682Smarkm#endif 258178825Sdfr if (file->major == NULL || file->minor == NULL) { 259178825Sdfr syslog(LOG_ERR, "out of memory"); 260178825Sdfr return -1; 261178825Sdfr } 262178825Sdfr } else { 263178825Sdfr if (asprintf(&file->size, "%lu", (unsigned long)st->st_size) == -1) 264178825Sdfr file->size = NULL; 265178825Sdfr } 26655682Smarkm 26755682Smarkm { 26855682Smarkm time_t t = time(NULL); 26972445Sassar time_t mtime = st->st_mtime; 27072445Sassar struct tm *tm = localtime(&mtime); 27172445Sassar if((t - mtime > 6*30*24*60*60) || 27272445Sassar (mtime - t > 6*30*24*60*60)) 27355682Smarkm strftime(buf, sizeof(buf), "%b %e %Y", tm); 27455682Smarkm else 27555682Smarkm strftime(buf, sizeof(buf), "%b %e %H:%M", tm); 27655682Smarkm file->date = strdup(buf); 277178825Sdfr if (file->date == NULL) { 278178825Sdfr syslog(LOG_ERR, "out of memory"); 279178825Sdfr return -1; 280178825Sdfr } 28155682Smarkm } 28255682Smarkm { 28355682Smarkm const char *p = strrchr(filename, '/'); 28455682Smarkm if(p) 28555682Smarkm p++; 28655682Smarkm else 28755682Smarkm p = filename; 288178825Sdfr if((flags & LS_TYPE) && file_type != 0) { 289178825Sdfr if (asprintf(&file->filename, "%s%c", p, file_type) == -1) 290178825Sdfr file->filename = NULL; 291178825Sdfr } else 29272445Sassar file->filename = strdup(p); 293178825Sdfr if (file->filename == NULL) { 294178825Sdfr syslog(LOG_ERR, "out of memory"); 295178825Sdfr return -1; 296178825Sdfr } 29755682Smarkm } 29855682Smarkm if(S_ISLNK(st->st_mode)) { 29955682Smarkm int n; 300120945Snectar n = readlink((char *)filename, buf, sizeof(buf) - 1); 30155682Smarkm if(n >= 0) { 30255682Smarkm buf[n] = '\0'; 30355682Smarkm file->link = strdup(buf); 304178825Sdfr if (file->link == NULL) { 305178825Sdfr syslog(LOG_ERR, "out of memory"); 306178825Sdfr return -1; 307178825Sdfr } 30855682Smarkm } else 30990926Snectar sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno)); 31055682Smarkm } 311178825Sdfr return 0; 31255682Smarkm} 31355682Smarkm 31455682Smarkmstatic void 31555682Smarkmprint_file(FILE *out, 31655682Smarkm int flags, 31755682Smarkm struct fileinfo *f, 31855682Smarkm int max_inode, 31955682Smarkm int max_bsize, 32055682Smarkm int max_n_link, 32155682Smarkm int max_user, 32255682Smarkm int max_group, 32355682Smarkm int max_size, 32455682Smarkm int max_major, 32555682Smarkm int max_minor, 32655682Smarkm int max_date) 32755682Smarkm{ 32855682Smarkm if(f->filename == NULL) 32955682Smarkm return; 33055682Smarkm 33155682Smarkm if(flags & LS_INODE) { 33255682Smarkm sec_fprintf2(out, "%*d", max_inode, f->inode); 33355682Smarkm sec_fprintf2(out, " "); 33455682Smarkm } 33555682Smarkm if(flags & LS_SIZE) { 33655682Smarkm sec_fprintf2(out, "%*d", max_bsize, f->bsize); 33755682Smarkm sec_fprintf2(out, " "); 33855682Smarkm } 33955682Smarkm sec_fprintf2(out, "%s", f->mode); 34055682Smarkm sec_fprintf2(out, " "); 34155682Smarkm sec_fprintf2(out, "%*d", max_n_link, f->n_link); 34255682Smarkm sec_fprintf2(out, " "); 34355682Smarkm sec_fprintf2(out, "%-*s", max_user, f->user); 34455682Smarkm sec_fprintf2(out, " "); 34555682Smarkm sec_fprintf2(out, "%-*s", max_group, f->group); 34655682Smarkm sec_fprintf2(out, " "); 34755682Smarkm if(f->major != NULL && f->minor != NULL) 34855682Smarkm sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); 34955682Smarkm else 35055682Smarkm sec_fprintf2(out, "%*s", max_size, f->size); 35155682Smarkm sec_fprintf2(out, " "); 35255682Smarkm sec_fprintf2(out, "%*s", max_date, f->date); 35355682Smarkm sec_fprintf2(out, " "); 35455682Smarkm sec_fprintf2(out, "%s", f->filename); 35555682Smarkm if(f->link) 35655682Smarkm sec_fprintf2(out, " -> %s", f->link); 35755682Smarkm sec_fprintf2(out, "\r\n"); 35855682Smarkm} 35955682Smarkm 36055682Smarkmstatic int 36155682Smarkmcompare_filename(struct fileinfo *a, struct fileinfo *b) 36255682Smarkm{ 36355682Smarkm if(a->filename == NULL) 36455682Smarkm return 1; 36555682Smarkm if(b->filename == NULL) 36655682Smarkm return -1; 36755682Smarkm return strcmp(a->filename, b->filename); 36855682Smarkm} 36955682Smarkm 37055682Smarkmstatic int 37155682Smarkmcompare_mtime(struct fileinfo *a, struct fileinfo *b) 37255682Smarkm{ 37355682Smarkm if(a->filename == NULL) 37455682Smarkm return 1; 37555682Smarkm if(b->filename == NULL) 37655682Smarkm return -1; 37772445Sassar return b->st.st_mtime - a->st.st_mtime; 37855682Smarkm} 37955682Smarkm 38055682Smarkmstatic int 38155682Smarkmcompare_size(struct fileinfo *a, struct fileinfo *b) 38255682Smarkm{ 38355682Smarkm if(a->filename == NULL) 38455682Smarkm return 1; 38555682Smarkm if(b->filename == NULL) 38655682Smarkm return -1; 38772445Sassar return b->st.st_size - a->st.st_size; 38855682Smarkm} 38955682Smarkm 390102644Snectarstatic int list_dir(FILE*, const char*, int); 39155682Smarkm 39255682Smarkmstatic int 393178825Sdfrfind_log10(int num) 39455682Smarkm{ 39555682Smarkm int i = 1; 39655682Smarkm while(num > 10) { 39755682Smarkm i++; 39855682Smarkm num /= 10; 39955682Smarkm } 40055682Smarkm return i; 40155682Smarkm} 40255682Smarkm 40355682Smarkm/* 40455682Smarkm * Operate as lstat but fake up entries for AFS mount points so we don't 40555682Smarkm * have to fetch them. 40655682Smarkm */ 40755682Smarkm 408233294Sstas#ifdef KRB5 40972445Sassarstatic int do_the_afs_dance = 1; 41072445Sassar#endif 41172445Sassar 41255682Smarkmstatic int 41355682Smarkmlstat_file (const char *file, struct stat *sb) 41455682Smarkm{ 415233294Sstas#ifdef KRB5 41672445Sassar if (do_the_afs_dance && 417233294Sstas k_hasafs() 41855682Smarkm && strcmp(file, ".") 41972445Sassar && strcmp(file, "..") 42072445Sassar && strcmp(file, "/")) 42155682Smarkm { 42255682Smarkm struct ViceIoctl a_params; 42372445Sassar char *dir, *last; 42455682Smarkm char *path_bkp; 42555682Smarkm static ino_t ino_counter = 0, ino_last = 0; 42655682Smarkm int ret; 42755682Smarkm const int maxsize = 2048; 428233294Sstas 42955682Smarkm path_bkp = strdup (file); 43055682Smarkm if (path_bkp == NULL) 43155682Smarkm return -1; 432233294Sstas 43355682Smarkm a_params.out = malloc (maxsize); 434233294Sstas if (a_params.out == NULL) { 43555682Smarkm free (path_bkp); 43655682Smarkm return -1; 43755682Smarkm } 438233294Sstas 43955682Smarkm /* If path contains more than the filename alone - split it */ 440233294Sstas 44155682Smarkm last = strrchr (path_bkp, '/'); 44255682Smarkm if (last != NULL) { 44372445Sassar if(last[1] == '\0') 44472445Sassar /* if path ended in /, replace with `.' */ 44572445Sassar a_params.in = "."; 44672445Sassar else 44772445Sassar a_params.in = last + 1; 44872445Sassar while(last > path_bkp && *--last == '/'); 44972445Sassar if(*last != '/' || last != path_bkp) { 45072445Sassar *++last = '\0'; 45172445Sassar dir = path_bkp; 45272445Sassar } else 45372445Sassar /* we got to the start, so this must be the root dir */ 45472445Sassar dir = "/"; 45572445Sassar } else { 45672445Sassar /* file is relative to cdir */ 45772445Sassar dir = "."; 45872445Sassar a_params.in = path_bkp; 45972445Sassar } 460233294Sstas 46155682Smarkm a_params.in_size = strlen (a_params.in) + 1; 46255682Smarkm a_params.out_size = maxsize; 463233294Sstas 46472445Sassar ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0); 46555682Smarkm free (a_params.out); 46655682Smarkm if (ret < 0) { 46755682Smarkm free (path_bkp); 46855682Smarkm 46955682Smarkm if (errno != EINVAL) 47055682Smarkm return ret; 47155682Smarkm else 47255682Smarkm /* if we get EINVAL this is probably not a mountpoint */ 47355682Smarkm return lstat (file, sb); 47455682Smarkm } 47555682Smarkm 476233294Sstas /* 47755682Smarkm * wow this was a mountpoint, lets cook the struct stat 47855682Smarkm * use . as a prototype 47955682Smarkm */ 48055682Smarkm 48172445Sassar ret = lstat (dir, sb); 48255682Smarkm free (path_bkp); 48355682Smarkm if (ret < 0) 48455682Smarkm return ret; 48555682Smarkm 48655682Smarkm if (ino_last == sb->st_ino) 48755682Smarkm ino_counter++; 48855682Smarkm else { 48955682Smarkm ino_last = sb->st_ino; 49055682Smarkm ino_counter = 0; 49155682Smarkm } 49255682Smarkm sb->st_ino += ino_counter; 49355682Smarkm sb->st_nlink = 3; 49455682Smarkm 49555682Smarkm return 0; 49655682Smarkm } 497233294Sstas#endif /* KRB5 */ 49855682Smarkm return lstat (file, sb); 49955682Smarkm} 50055682Smarkm 50190926Snectar#define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \ 50290926Snectar ((X)[1] == '.' && (X)[2] == '\0'))) 50390926Snectar 504102644Snectarstatic int 50555682Smarkmlist_files(FILE *out, const char **files, int n_files, int flags) 50655682Smarkm{ 50755682Smarkm struct fileinfo *fi; 50855682Smarkm int i; 50990926Snectar int *dirs = NULL; 51090926Snectar size_t total_blocks = 0; 51190926Snectar int n_print = 0; 512102644Snectar int ret = 0; 51355682Smarkm 514102644Snectar if(n_files == 0) 515102644Snectar return 0; 516102644Snectar 51790926Snectar if(n_files > 1) 51890926Snectar flags |= LS_SHOW_DIRNAME; 51990926Snectar 52055682Smarkm fi = calloc(n_files, sizeof(*fi)); 52155682Smarkm if (fi == NULL) { 522102644Snectar syslog(LOG_ERR, "out of memory"); 523102644Snectar return -1; 52455682Smarkm } 52555682Smarkm for(i = 0; i < n_files; i++) { 52655682Smarkm if(lstat_file(files[i], &fi[i].st) < 0) { 52755682Smarkm sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); 52855682Smarkm fi[i].filename = NULL; 52955682Smarkm } else { 53090926Snectar int include_in_list = 1; 53190926Snectar total_blocks += block_convert(fi[i].st.st_blocks); 53290926Snectar if(S_ISDIR(fi[i].st.st_mode)) { 53390926Snectar if(dirs == NULL) 53490926Snectar dirs = calloc(n_files, sizeof(*dirs)); 53590926Snectar if(dirs == NULL) { 536102644Snectar syslog(LOG_ERR, "%s: %m", files[i]); 537102644Snectar ret = -1; 53890926Snectar goto out; 53990926Snectar } 54090926Snectar dirs[i] = 1; 54190926Snectar if((flags & LS_DIRS) == 0) 54290926Snectar include_in_list = 0; 54355682Smarkm } 54490926Snectar if(include_in_list) { 545178825Sdfr ret = make_fileinfo(out, files[i], &fi[i], flags); 546178825Sdfr if (ret) 547178825Sdfr goto out; 54890926Snectar n_print++; 54990926Snectar } 55055682Smarkm } 55155682Smarkm } 55255682Smarkm switch(SORT_MODE(flags)) { 55355682Smarkm case LS_SORT_NAME: 554233294Sstas qsort(fi, n_files, sizeof(*fi), 55555682Smarkm (int (*)(const void*, const void*))compare_filename); 55655682Smarkm break; 55755682Smarkm case LS_SORT_MTIME: 558233294Sstas qsort(fi, n_files, sizeof(*fi), 55955682Smarkm (int (*)(const void*, const void*))compare_mtime); 56055682Smarkm break; 56155682Smarkm case LS_SORT_SIZE: 562233294Sstas qsort(fi, n_files, sizeof(*fi), 56355682Smarkm (int (*)(const void*, const void*))compare_size); 56455682Smarkm break; 56555682Smarkm } 56672445Sassar if(DISP_MODE(flags) == LS_DISP_LONG) { 56755682Smarkm int max_inode = 0; 56855682Smarkm int max_bsize = 0; 56955682Smarkm int max_n_link = 0; 57055682Smarkm int max_user = 0; 57155682Smarkm int max_group = 0; 57255682Smarkm int max_size = 0; 57355682Smarkm int max_major = 0; 57455682Smarkm int max_minor = 0; 57555682Smarkm int max_date = 0; 57655682Smarkm for(i = 0; i < n_files; i++) { 57755682Smarkm if(fi[i].filename == NULL) 57855682Smarkm continue; 57955682Smarkm if(fi[i].inode > max_inode) 58055682Smarkm max_inode = fi[i].inode; 58155682Smarkm if(fi[i].bsize > max_bsize) 58255682Smarkm max_bsize = fi[i].bsize; 58355682Smarkm if(fi[i].n_link > max_n_link) 58455682Smarkm max_n_link = fi[i].n_link; 58555682Smarkm if(strlen(fi[i].user) > max_user) 58655682Smarkm max_user = strlen(fi[i].user); 58755682Smarkm if(strlen(fi[i].group) > max_group) 58855682Smarkm max_group = strlen(fi[i].group); 58955682Smarkm if(fi[i].major != NULL && strlen(fi[i].major) > max_major) 59055682Smarkm max_major = strlen(fi[i].major); 59155682Smarkm if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) 59255682Smarkm max_minor = strlen(fi[i].minor); 59355682Smarkm if(fi[i].size != NULL && strlen(fi[i].size) > max_size) 59455682Smarkm max_size = strlen(fi[i].size); 59555682Smarkm if(strlen(fi[i].date) > max_date) 59655682Smarkm max_date = strlen(fi[i].date); 59755682Smarkm } 59855682Smarkm if(max_size < max_major + max_minor + 2) 59955682Smarkm max_size = max_major + max_minor + 2; 60055682Smarkm else if(max_size - max_minor - 2 > max_major) 60155682Smarkm max_major = max_size - max_minor - 2; 602178825Sdfr max_inode = find_log10(max_inode); 603178825Sdfr max_bsize = find_log10(max_bsize); 604178825Sdfr max_n_link = find_log10(max_n_link); 605233294Sstas 60690926Snectar if(n_print > 0) 60790926Snectar sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks); 60855682Smarkm if(flags & LS_SORT_REVERSE) 60955682Smarkm for(i = n_files - 1; i >= 0; i--) 61055682Smarkm print_file(out, 61155682Smarkm flags, 61255682Smarkm &fi[i], 61355682Smarkm max_inode, 61455682Smarkm max_bsize, 61555682Smarkm max_n_link, 61655682Smarkm max_user, 61755682Smarkm max_group, 61855682Smarkm max_size, 61955682Smarkm max_major, 62055682Smarkm max_minor, 62155682Smarkm max_date); 62255682Smarkm else 62355682Smarkm for(i = 0; i < n_files; i++) 62455682Smarkm print_file(out, 62555682Smarkm flags, 62655682Smarkm &fi[i], 62755682Smarkm max_inode, 62855682Smarkm max_bsize, 62955682Smarkm max_n_link, 63055682Smarkm max_user, 63155682Smarkm max_group, 63255682Smarkm max_size, 63355682Smarkm max_major, 63455682Smarkm max_minor, 63555682Smarkm max_date); 636233294Sstas } else if(DISP_MODE(flags) == LS_DISP_COLUMN || 63772445Sassar DISP_MODE(flags) == LS_DISP_CROSS) { 63872445Sassar int max_len = 0; 63990926Snectar int size_len = 0; 64072445Sassar int num_files = n_files; 64172445Sassar int columns; 64272445Sassar int j; 64372445Sassar for(i = 0; i < n_files; i++) { 64472445Sassar if(fi[i].filename == NULL) { 64572445Sassar num_files--; 64672445Sassar continue; 64772445Sassar } 64872445Sassar if(strlen(fi[i].filename) > max_len) 64972445Sassar max_len = strlen(fi[i].filename); 650178825Sdfr if(find_log10(fi[i].bsize) > size_len) 651178825Sdfr size_len = find_log10(fi[i].bsize); 65272445Sassar } 65390926Snectar if(num_files == 0) 65490926Snectar goto next; 65590926Snectar if(flags & LS_SIZE) { 65690926Snectar columns = 80 / (size_len + 1 + max_len + 1); 65790926Snectar max_len = 80 / columns - size_len - 1; 65890926Snectar } else { 65990926Snectar columns = 80 / (max_len + 1); /* get space between columns */ 66090926Snectar max_len = 80 / columns; 66190926Snectar } 66290926Snectar if(flags & LS_SIZE) 663233294Sstas sec_fprintf2(out, "total %lu\r\n", 66490926Snectar (unsigned long)total_blocks); 66572445Sassar if(DISP_MODE(flags) == LS_DISP_CROSS) { 66672445Sassar for(i = 0, j = 0; i < n_files; i++) { 66772445Sassar if(fi[i].filename == NULL) 66872445Sassar continue; 66990926Snectar if(flags & LS_SIZE) 670233294Sstas sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize, 67190926Snectar max_len, fi[i].filename); 67290926Snectar else 67390926Snectar sec_fprintf2(out, "%-*s", max_len, fi[i].filename); 67472445Sassar j++; 67572445Sassar if(j == columns) { 67672445Sassar sec_fprintf2(out, "\r\n"); 67772445Sassar j = 0; 67872445Sassar } 67972445Sassar } 68072445Sassar if(j > 0) 68190926Snectar sec_fprintf2(out, "\r\n"); 68272445Sassar } else { 68372445Sassar int skip = (num_files + columns - 1) / columns; 684233294Sstas 68572445Sassar for(i = 0; i < skip; i++) { 68672445Sassar for(j = i; j < n_files;) { 68772445Sassar while(j < n_files && fi[j].filename == NULL) 68872445Sassar j++; 68990926Snectar if(flags & LS_SIZE) 690233294Sstas sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize, 69190926Snectar max_len, fi[j].filename); 69290926Snectar else 69390926Snectar sec_fprintf2(out, "%-*s", max_len, fi[j].filename); 69472445Sassar j += skip; 69572445Sassar } 69672445Sassar sec_fprintf2(out, "\r\n"); 69772445Sassar } 69872445Sassar } 69972445Sassar } else { 70072445Sassar for(i = 0; i < n_files; i++) { 70172445Sassar if(fi[i].filename == NULL) 70272445Sassar continue; 70372445Sassar sec_fprintf2(out, "%s\r\n", fi[i].filename); 70472445Sassar } 70555682Smarkm } 70690926Snectar next: 70790926Snectar if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) { 70890926Snectar for(i = 0; i < n_files; i++) { 70990926Snectar if(dirs[i]) { 71090926Snectar const char *p = strrchr(files[i], '/'); 71190926Snectar if(p == NULL) 71290926Snectar p = files[i]; 713233294Sstas else 71490926Snectar p++; 71590926Snectar if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) { 71690926Snectar if((flags & LS_SHOW_DIRNAME)) { 71790926Snectar if ((flags & LS_EXTRA_BLANK)) 71890926Snectar sec_fprintf2(out, "\r\n"); 71990926Snectar sec_fprintf2(out, "%s:\r\n", files[i]); 72090926Snectar } 72190926Snectar list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK); 72290926Snectar } 72390926Snectar } 72490926Snectar } 72590926Snectar } 72690926Snectar out: 72772445Sassar for(i = 0; i < n_files; i++) 72872445Sassar free_fileinfo(&fi[i]); 72972445Sassar free(fi); 73090926Snectar if(dirs != NULL) 73190926Snectar free(dirs); 732102644Snectar return ret; 73355682Smarkm} 73455682Smarkm 73555682Smarkmstatic void 73655682Smarkmfree_files (char **files, int n) 73755682Smarkm{ 73855682Smarkm int i; 73955682Smarkm 74055682Smarkm for (i = 0; i < n; ++i) 74155682Smarkm free (files[i]); 74255682Smarkm free (files); 74355682Smarkm} 74455682Smarkm 74590926Snectarstatic int 74690926Snectarhide_file(const char *filename, int flags) 74790926Snectar{ 74890926Snectar if(filename[0] != '.') 74990926Snectar return 0; 75090926Snectar if((flags & LS_IGNORE_DOT)) 75190926Snectar return 1; 75290926Snectar if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) { 75390926Snectar if((flags & LS_SHOW_ALL)) 75490926Snectar return 0; 75590926Snectar else 75690926Snectar return 1; 75790926Snectar } 75890926Snectar return 0; 75990926Snectar} 76090926Snectar 761102644Snectarstatic int 76255682Smarkmlist_dir(FILE *out, const char *directory, int flags) 76355682Smarkm{ 76455682Smarkm DIR *d = opendir(directory); 76555682Smarkm struct dirent *ent; 76655682Smarkm char **files = NULL; 76755682Smarkm int n_files = 0; 768178825Sdfr int ret; 76955682Smarkm 77055682Smarkm if(d == NULL) { 771102644Snectar syslog(LOG_ERR, "%s: %m", directory); 772102644Snectar return -1; 77355682Smarkm } 77455682Smarkm while((ent = readdir(d)) != NULL) { 77555682Smarkm void *tmp; 77655682Smarkm 77790926Snectar if(hide_file(ent->d_name, flags)) 77890926Snectar continue; 77955682Smarkm tmp = realloc(files, (n_files + 1) * sizeof(*files)); 78055682Smarkm if (tmp == NULL) { 781102644Snectar syslog(LOG_ERR, "%s: out of memory", directory); 78255682Smarkm free_files (files, n_files); 78355682Smarkm closedir (d); 784102644Snectar return -1; 78555682Smarkm } 78655682Smarkm files = tmp; 787178825Sdfr ret = asprintf(&files[n_files], "%s/%s", directory, ent->d_name); 788178825Sdfr if (ret == -1) { 789102644Snectar syslog(LOG_ERR, "%s: out of memory", directory); 79055682Smarkm free_files (files, n_files); 79155682Smarkm closedir (d); 792102644Snectar return -1; 79355682Smarkm } 79455682Smarkm ++n_files; 79555682Smarkm } 79655682Smarkm closedir(d); 797102644Snectar return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG); 79855682Smarkm} 79955682Smarkm 80090926Snectarstatic int 80190926Snectarparse_flags(const char *options) 80290926Snectar{ 80390926Snectar#ifdef TEST 80490926Snectar int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN; 80590926Snectar#else 80690926Snectar int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG; 80790926Snectar#endif 80890926Snectar 80990926Snectar const char *p; 81090926Snectar if(options == NULL || *options != '-') 81190926Snectar return flags; 81290926Snectar for(p = options + 1; *p; p++) { 81390926Snectar switch(*p) { 81490926Snectar case '1': 81590926Snectar flags = (flags & ~LS_DISP_MODE); 81690926Snectar break; 81790926Snectar case 'a': 81890926Snectar flags |= LS_SHOW_ALL; 81990926Snectar /*FALLTHROUGH*/ 82090926Snectar case 'A': 82190926Snectar flags &= ~LS_IGNORE_DOT; 82290926Snectar break; 82390926Snectar case 'C': 82490926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN; 82590926Snectar break; 82690926Snectar case 'd': 82790926Snectar flags |= LS_DIRS; 82890926Snectar break; 82990926Snectar case 'f': 83090926Snectar flags = (flags & ~LS_SORT_MODE); 83190926Snectar break; 83290926Snectar case 'F': 83390926Snectar flags |= LS_TYPE; 83490926Snectar break; 83590926Snectar case 'i': 83690926Snectar flags |= LS_INODE; 83790926Snectar break; 83890926Snectar case 'l': 83990926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG; 84090926Snectar break; 84190926Snectar case 'r': 84290926Snectar flags |= LS_SORT_REVERSE; 84390926Snectar break; 84490926Snectar case 'R': 84590926Snectar flags |= LS_RECURSIVE; 84690926Snectar break; 84790926Snectar case 's': 84890926Snectar flags |= LS_SIZE; 84990926Snectar break; 85090926Snectar case 'S': 85190926Snectar flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; 85290926Snectar break; 85390926Snectar case 't': 85490926Snectar flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; 85590926Snectar break; 85690926Snectar case 'x': 85790926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS; 85890926Snectar break; 85990926Snectar /* these are a bunch of unimplemented flags from BSD ls */ 86090926Snectar case 'k': /* display sizes in kB */ 86190926Snectar case 'c': /* last change time */ 86290926Snectar case 'L': /* list symlink target */ 86390926Snectar case 'm': /* stream output */ 86490926Snectar case 'o': /* BSD file flags */ 86590926Snectar case 'p': /* display / after directories */ 86690926Snectar case 'q': /* print non-graphic characters */ 86790926Snectar case 'u': /* use last access time */ 86890926Snectar case 'T': /* display complete time */ 86990926Snectar case 'W': /* include whiteouts */ 87090926Snectar break; 87190926Snectar } 87290926Snectar } 87390926Snectar return flags; 87490926Snectar} 87590926Snectar 876102644Snectarint 87755682Smarkmbuiltin_ls(FILE *out, const char *file) 87855682Smarkm{ 87990926Snectar int flags; 880102644Snectar int ret; 88155682Smarkm 88255682Smarkm if(*file == '-') { 88390926Snectar flags = parse_flags(file); 88455682Smarkm file = "."; 88590926Snectar } else 88690926Snectar flags = parse_flags(""); 88790926Snectar 888102644Snectar ret = list_files(out, &file, 1, flags); 88955682Smarkm sec_fflush(out); 890102644Snectar return ret; 89155682Smarkm} 892