ls.c revision 90926
155682Smarkm/* 290926Snectar * Copyright (c) 1999 - 2001 Kungliga Tekniska H�gskolan 355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden). 455682Smarkm * All rights reserved. 555682Smarkm * 655682Smarkm * Redistribution and use in source and binary forms, with or without 755682Smarkm * modification, are permitted provided that the following conditions 855682Smarkm * are met: 955682Smarkm * 1055682Smarkm * 1. Redistributions of source code must retain the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1455682Smarkm * notice, this list of conditions and the following disclaimer in the 1555682Smarkm * 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 3690926SnectarRCSID("$Id: ls.c,v 1.23 2001/09/14 11:32:52 joda Exp $"); 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 12755682Smarkm#ifndef S_ISSOCK 12855682Smarkm#define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) 12955682Smarkm#endif 13055682Smarkm 13155682Smarkm#ifndef S_ISLNK 13255682Smarkm#define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) 13355682Smarkm#endif 13455682Smarkm 13590926Snectarstatic size_t 13690926Snectarblock_convert(size_t blocks) 13790926Snectar{ 13890926Snectar#ifdef S_BLKSIZE 13990926Snectar return blocks * S_BLKSIZE / 1024; 14090926Snectar#else 14190926Snectar return blocks * 512 / 1024; 14290926Snectar#endif 14390926Snectar} 14490926Snectar 14555682Smarkmstatic void 14690926Snectarmake_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags) 14755682Smarkm{ 14855682Smarkm char buf[128]; 14972445Sassar int file_type = 0; 15055682Smarkm struct stat *st = &file->st; 15155682Smarkm 15255682Smarkm file->inode = st->st_ino; 15390926Snectar file->bsize = block_convert(st->st_blocks); 15455682Smarkm 15572445Sassar if(S_ISDIR(st->st_mode)) { 15655682Smarkm file->mode[0] = 'd'; 15772445Sassar file_type = '/'; 15872445Sassar } 15955682Smarkm else if(S_ISCHR(st->st_mode)) 16055682Smarkm file->mode[0] = 'c'; 16155682Smarkm else if(S_ISBLK(st->st_mode)) 16255682Smarkm file->mode[0] = 'b'; 16372445Sassar else if(S_ISREG(st->st_mode)) { 16455682Smarkm file->mode[0] = '-'; 16572445Sassar if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 16672445Sassar file_type = '*'; 16772445Sassar } 16872445Sassar else if(S_ISFIFO(st->st_mode)) { 16955682Smarkm file->mode[0] = 'p'; 17072445Sassar file_type = '|'; 17172445Sassar } 17272445Sassar else if(S_ISLNK(st->st_mode)) { 17355682Smarkm file->mode[0] = 'l'; 17472445Sassar file_type = '@'; 17572445Sassar } 17672445Sassar else if(S_ISSOCK(st->st_mode)) { 17755682Smarkm file->mode[0] = 's'; 17872445Sassar file_type = '='; 17972445Sassar } 18055682Smarkm#ifdef S_ISWHT 18172445Sassar else if(S_ISWHT(st->st_mode)) { 18255682Smarkm file->mode[0] = 'w'; 18372445Sassar file_type = '%'; 18472445Sassar } 18555682Smarkm#endif 18655682Smarkm else 18755682Smarkm file->mode[0] = '?'; 18855682Smarkm { 18955682Smarkm char *x[] = { "---", "--x", "-w-", "-wx", 19055682Smarkm "r--", "r-x", "rw-", "rwx" }; 19155682Smarkm strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); 19255682Smarkm strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); 19355682Smarkm strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); 19455682Smarkm if((st->st_mode & S_ISUID)) { 19555682Smarkm if((st->st_mode & S_IXUSR)) 19655682Smarkm file->mode[3] = 's'; 19755682Smarkm else 19855682Smarkm file->mode[3] = 'S'; 19955682Smarkm } 20055682Smarkm if((st->st_mode & S_ISGID)) { 20155682Smarkm if((st->st_mode & S_IXGRP)) 20255682Smarkm file->mode[6] = 's'; 20355682Smarkm else 20455682Smarkm file->mode[6] = 'S'; 20555682Smarkm } 20655682Smarkm if((st->st_mode & S_ISTXT)) { 20755682Smarkm if((st->st_mode & S_IXOTH)) 20855682Smarkm file->mode[9] = 't'; 20955682Smarkm else 21055682Smarkm file->mode[9] = 'T'; 21155682Smarkm } 21255682Smarkm } 21355682Smarkm file->n_link = st->st_nlink; 21455682Smarkm { 21555682Smarkm struct passwd *pwd; 21655682Smarkm pwd = getpwuid(st->st_uid); 21755682Smarkm if(pwd == NULL) 21855682Smarkm asprintf(&file->user, "%u", (unsigned)st->st_uid); 21955682Smarkm else 22055682Smarkm file->user = strdup(pwd->pw_name); 22155682Smarkm } 22255682Smarkm { 22355682Smarkm struct group *grp; 22455682Smarkm grp = getgrgid(st->st_gid); 22555682Smarkm if(grp == NULL) 22655682Smarkm asprintf(&file->group, "%u", (unsigned)st->st_gid); 22755682Smarkm else 22855682Smarkm file->group = strdup(grp->gr_name); 22955682Smarkm } 23055682Smarkm 23155682Smarkm if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 23255682Smarkm#if defined(major) && defined(minor) 23355682Smarkm asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)); 23455682Smarkm asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)); 23555682Smarkm#else 23655682Smarkm /* Don't want to use the DDI/DKI crap. */ 23755682Smarkm asprintf(&file->major, "%u", (unsigned)st->st_rdev); 23855682Smarkm asprintf(&file->minor, "%u", 0); 23955682Smarkm#endif 24055682Smarkm } else 24155682Smarkm asprintf(&file->size, "%lu", (unsigned long)st->st_size); 24255682Smarkm 24355682Smarkm { 24455682Smarkm time_t t = time(NULL); 24572445Sassar time_t mtime = st->st_mtime; 24672445Sassar struct tm *tm = localtime(&mtime); 24772445Sassar if((t - mtime > 6*30*24*60*60) || 24872445Sassar (mtime - t > 6*30*24*60*60)) 24955682Smarkm strftime(buf, sizeof(buf), "%b %e %Y", tm); 25055682Smarkm else 25155682Smarkm strftime(buf, sizeof(buf), "%b %e %H:%M", tm); 25255682Smarkm file->date = strdup(buf); 25355682Smarkm } 25455682Smarkm { 25555682Smarkm const char *p = strrchr(filename, '/'); 25655682Smarkm if(p) 25755682Smarkm p++; 25855682Smarkm else 25955682Smarkm p = filename; 26072445Sassar if((flags & LS_TYPE) && file_type != 0) 26172445Sassar asprintf(&file->filename, "%s%c", p, file_type); 26272445Sassar else 26372445Sassar file->filename = strdup(p); 26455682Smarkm } 26555682Smarkm if(S_ISLNK(st->st_mode)) { 26655682Smarkm int n; 26755682Smarkm n = readlink((char *)filename, buf, sizeof(buf)); 26855682Smarkm if(n >= 0) { 26955682Smarkm buf[n] = '\0'; 27055682Smarkm file->link = strdup(buf); 27155682Smarkm } else 27290926Snectar sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno)); 27355682Smarkm } 27455682Smarkm} 27555682Smarkm 27655682Smarkmstatic void 27755682Smarkmprint_file(FILE *out, 27855682Smarkm int flags, 27955682Smarkm struct fileinfo *f, 28055682Smarkm int max_inode, 28155682Smarkm int max_bsize, 28255682Smarkm int max_n_link, 28355682Smarkm int max_user, 28455682Smarkm int max_group, 28555682Smarkm int max_size, 28655682Smarkm int max_major, 28755682Smarkm int max_minor, 28855682Smarkm int max_date) 28955682Smarkm{ 29055682Smarkm if(f->filename == NULL) 29155682Smarkm return; 29255682Smarkm 29355682Smarkm if(flags & LS_INODE) { 29455682Smarkm sec_fprintf2(out, "%*d", max_inode, f->inode); 29555682Smarkm sec_fprintf2(out, " "); 29655682Smarkm } 29755682Smarkm if(flags & LS_SIZE) { 29855682Smarkm sec_fprintf2(out, "%*d", max_bsize, f->bsize); 29955682Smarkm sec_fprintf2(out, " "); 30055682Smarkm } 30155682Smarkm sec_fprintf2(out, "%s", f->mode); 30255682Smarkm sec_fprintf2(out, " "); 30355682Smarkm sec_fprintf2(out, "%*d", max_n_link, f->n_link); 30455682Smarkm sec_fprintf2(out, " "); 30555682Smarkm sec_fprintf2(out, "%-*s", max_user, f->user); 30655682Smarkm sec_fprintf2(out, " "); 30755682Smarkm sec_fprintf2(out, "%-*s", max_group, f->group); 30855682Smarkm sec_fprintf2(out, " "); 30955682Smarkm if(f->major != NULL && f->minor != NULL) 31055682Smarkm sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); 31155682Smarkm else 31255682Smarkm sec_fprintf2(out, "%*s", max_size, f->size); 31355682Smarkm sec_fprintf2(out, " "); 31455682Smarkm sec_fprintf2(out, "%*s", max_date, f->date); 31555682Smarkm sec_fprintf2(out, " "); 31655682Smarkm sec_fprintf2(out, "%s", f->filename); 31755682Smarkm if(f->link) 31855682Smarkm sec_fprintf2(out, " -> %s", f->link); 31955682Smarkm sec_fprintf2(out, "\r\n"); 32055682Smarkm} 32155682Smarkm 32255682Smarkmstatic int 32355682Smarkmcompare_filename(struct fileinfo *a, struct fileinfo *b) 32455682Smarkm{ 32555682Smarkm if(a->filename == NULL) 32655682Smarkm return 1; 32755682Smarkm if(b->filename == NULL) 32855682Smarkm return -1; 32955682Smarkm return strcmp(a->filename, b->filename); 33055682Smarkm} 33155682Smarkm 33255682Smarkmstatic int 33355682Smarkmcompare_mtime(struct fileinfo *a, struct fileinfo *b) 33455682Smarkm{ 33555682Smarkm if(a->filename == NULL) 33655682Smarkm return 1; 33755682Smarkm if(b->filename == NULL) 33855682Smarkm return -1; 33972445Sassar return b->st.st_mtime - a->st.st_mtime; 34055682Smarkm} 34155682Smarkm 34255682Smarkmstatic int 34355682Smarkmcompare_size(struct fileinfo *a, struct fileinfo *b) 34455682Smarkm{ 34555682Smarkm if(a->filename == NULL) 34655682Smarkm return 1; 34755682Smarkm if(b->filename == NULL) 34855682Smarkm return -1; 34972445Sassar return b->st.st_size - a->st.st_size; 35055682Smarkm} 35155682Smarkm 35255682Smarkmstatic void 35355682Smarkmlist_dir(FILE *out, const char *directory, int flags); 35455682Smarkm 35555682Smarkmstatic int 35655682Smarkmlog10(int num) 35755682Smarkm{ 35855682Smarkm int i = 1; 35955682Smarkm while(num > 10) { 36055682Smarkm i++; 36155682Smarkm num /= 10; 36255682Smarkm } 36355682Smarkm return i; 36455682Smarkm} 36555682Smarkm 36655682Smarkm/* 36755682Smarkm * Operate as lstat but fake up entries for AFS mount points so we don't 36855682Smarkm * have to fetch them. 36955682Smarkm */ 37055682Smarkm 37172445Sassar#ifdef KRB4 37272445Sassarstatic int do_the_afs_dance = 1; 37372445Sassar#endif 37472445Sassar 37555682Smarkmstatic int 37655682Smarkmlstat_file (const char *file, struct stat *sb) 37755682Smarkm{ 37855682Smarkm#ifdef KRB4 37972445Sassar if (do_the_afs_dance && 38072445Sassar k_hasafs() 38155682Smarkm && strcmp(file, ".") 38272445Sassar && strcmp(file, "..") 38372445Sassar && strcmp(file, "/")) 38455682Smarkm { 38555682Smarkm struct ViceIoctl a_params; 38672445Sassar char *dir, *last; 38755682Smarkm char *path_bkp; 38855682Smarkm static ino_t ino_counter = 0, ino_last = 0; 38955682Smarkm int ret; 39055682Smarkm const int maxsize = 2048; 39155682Smarkm 39255682Smarkm path_bkp = strdup (file); 39355682Smarkm if (path_bkp == NULL) 39455682Smarkm return -1; 39555682Smarkm 39655682Smarkm a_params.out = malloc (maxsize); 39755682Smarkm if (a_params.out == NULL) { 39855682Smarkm free (path_bkp); 39955682Smarkm return -1; 40055682Smarkm } 40155682Smarkm 40255682Smarkm /* If path contains more than the filename alone - split it */ 40355682Smarkm 40455682Smarkm last = strrchr (path_bkp, '/'); 40555682Smarkm if (last != NULL) { 40672445Sassar if(last[1] == '\0') 40772445Sassar /* if path ended in /, replace with `.' */ 40872445Sassar a_params.in = "."; 40972445Sassar else 41072445Sassar a_params.in = last + 1; 41172445Sassar while(last > path_bkp && *--last == '/'); 41272445Sassar if(*last != '/' || last != path_bkp) { 41372445Sassar *++last = '\0'; 41472445Sassar dir = path_bkp; 41572445Sassar } else 41672445Sassar /* we got to the start, so this must be the root dir */ 41772445Sassar dir = "/"; 41872445Sassar } else { 41972445Sassar /* file is relative to cdir */ 42072445Sassar dir = "."; 42172445Sassar a_params.in = path_bkp; 42272445Sassar } 42355682Smarkm 42455682Smarkm a_params.in_size = strlen (a_params.in) + 1; 42555682Smarkm a_params.out_size = maxsize; 42655682Smarkm 42772445Sassar ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0); 42855682Smarkm free (a_params.out); 42955682Smarkm if (ret < 0) { 43055682Smarkm free (path_bkp); 43155682Smarkm 43255682Smarkm if (errno != EINVAL) 43355682Smarkm return ret; 43455682Smarkm else 43555682Smarkm /* if we get EINVAL this is probably not a mountpoint */ 43655682Smarkm return lstat (file, sb); 43755682Smarkm } 43855682Smarkm 43955682Smarkm /* 44055682Smarkm * wow this was a mountpoint, lets cook the struct stat 44155682Smarkm * use . as a prototype 44255682Smarkm */ 44355682Smarkm 44472445Sassar ret = lstat (dir, sb); 44555682Smarkm free (path_bkp); 44655682Smarkm if (ret < 0) 44755682Smarkm return ret; 44855682Smarkm 44955682Smarkm if (ino_last == sb->st_ino) 45055682Smarkm ino_counter++; 45155682Smarkm else { 45255682Smarkm ino_last = sb->st_ino; 45355682Smarkm ino_counter = 0; 45455682Smarkm } 45555682Smarkm sb->st_ino += ino_counter; 45655682Smarkm sb->st_nlink = 3; 45755682Smarkm 45855682Smarkm return 0; 45955682Smarkm } 46055682Smarkm#endif /* KRB4 */ 46155682Smarkm return lstat (file, sb); 46255682Smarkm} 46355682Smarkm 46490926Snectar#define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \ 46590926Snectar ((X)[1] == '.' && (X)[2] == '\0'))) 46690926Snectar 46755682Smarkmstatic void 46855682Smarkmlist_files(FILE *out, const char **files, int n_files, int flags) 46955682Smarkm{ 47055682Smarkm struct fileinfo *fi; 47155682Smarkm int i; 47290926Snectar int *dirs = NULL; 47390926Snectar size_t total_blocks = 0; 47490926Snectar int n_print = 0; 47555682Smarkm 47690926Snectar if(n_files > 1) 47790926Snectar flags |= LS_SHOW_DIRNAME; 47890926Snectar 47955682Smarkm fi = calloc(n_files, sizeof(*fi)); 48055682Smarkm if (fi == NULL) { 48155682Smarkm sec_fprintf2(out, "ouf of memory\r\n"); 48255682Smarkm return; 48355682Smarkm } 48455682Smarkm for(i = 0; i < n_files; i++) { 48555682Smarkm if(lstat_file(files[i], &fi[i].st) < 0) { 48655682Smarkm sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); 48755682Smarkm fi[i].filename = NULL; 48855682Smarkm } else { 48990926Snectar int include_in_list = 1; 49090926Snectar total_blocks += block_convert(fi[i].st.st_blocks); 49190926Snectar if(S_ISDIR(fi[i].st.st_mode)) { 49290926Snectar if(dirs == NULL) 49390926Snectar dirs = calloc(n_files, sizeof(*dirs)); 49490926Snectar if(dirs == NULL) { 49590926Snectar sec_fprintf2(out, "%s: %s\r\n", 49690926Snectar files[i], strerror(errno)); 49790926Snectar goto out; 49890926Snectar } 49990926Snectar dirs[i] = 1; 50090926Snectar if((flags & LS_DIRS) == 0) 50190926Snectar include_in_list = 0; 50255682Smarkm } 50390926Snectar if(include_in_list) { 50490926Snectar make_fileinfo(out, files[i], &fi[i], flags); 50590926Snectar n_print++; 50690926Snectar } 50755682Smarkm } 50855682Smarkm } 50955682Smarkm switch(SORT_MODE(flags)) { 51055682Smarkm case LS_SORT_NAME: 51155682Smarkm qsort(fi, n_files, sizeof(*fi), 51255682Smarkm (int (*)(const void*, const void*))compare_filename); 51355682Smarkm break; 51455682Smarkm case LS_SORT_MTIME: 51555682Smarkm qsort(fi, n_files, sizeof(*fi), 51655682Smarkm (int (*)(const void*, const void*))compare_mtime); 51755682Smarkm break; 51855682Smarkm case LS_SORT_SIZE: 51955682Smarkm qsort(fi, n_files, sizeof(*fi), 52055682Smarkm (int (*)(const void*, const void*))compare_size); 52155682Smarkm break; 52255682Smarkm } 52372445Sassar if(DISP_MODE(flags) == LS_DISP_LONG) { 52455682Smarkm int max_inode = 0; 52555682Smarkm int max_bsize = 0; 52655682Smarkm int max_n_link = 0; 52755682Smarkm int max_user = 0; 52855682Smarkm int max_group = 0; 52955682Smarkm int max_size = 0; 53055682Smarkm int max_major = 0; 53155682Smarkm int max_minor = 0; 53255682Smarkm int max_date = 0; 53355682Smarkm for(i = 0; i < n_files; i++) { 53455682Smarkm if(fi[i].filename == NULL) 53555682Smarkm continue; 53655682Smarkm if(fi[i].inode > max_inode) 53755682Smarkm max_inode = fi[i].inode; 53855682Smarkm if(fi[i].bsize > max_bsize) 53955682Smarkm max_bsize = fi[i].bsize; 54055682Smarkm if(fi[i].n_link > max_n_link) 54155682Smarkm max_n_link = fi[i].n_link; 54255682Smarkm if(strlen(fi[i].user) > max_user) 54355682Smarkm max_user = strlen(fi[i].user); 54455682Smarkm if(strlen(fi[i].group) > max_group) 54555682Smarkm max_group = strlen(fi[i].group); 54655682Smarkm if(fi[i].major != NULL && strlen(fi[i].major) > max_major) 54755682Smarkm max_major = strlen(fi[i].major); 54855682Smarkm if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) 54955682Smarkm max_minor = strlen(fi[i].minor); 55055682Smarkm if(fi[i].size != NULL && strlen(fi[i].size) > max_size) 55155682Smarkm max_size = strlen(fi[i].size); 55255682Smarkm if(strlen(fi[i].date) > max_date) 55355682Smarkm max_date = strlen(fi[i].date); 55455682Smarkm } 55555682Smarkm if(max_size < max_major + max_minor + 2) 55655682Smarkm max_size = max_major + max_minor + 2; 55755682Smarkm else if(max_size - max_minor - 2 > max_major) 55855682Smarkm max_major = max_size - max_minor - 2; 55955682Smarkm max_inode = log10(max_inode); 56055682Smarkm max_bsize = log10(max_bsize); 56155682Smarkm max_n_link = log10(max_n_link); 56290926Snectar 56390926Snectar if(n_print > 0) 56490926Snectar sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks); 56555682Smarkm if(flags & LS_SORT_REVERSE) 56655682Smarkm for(i = n_files - 1; i >= 0; i--) 56755682Smarkm print_file(out, 56855682Smarkm flags, 56955682Smarkm &fi[i], 57055682Smarkm max_inode, 57155682Smarkm max_bsize, 57255682Smarkm max_n_link, 57355682Smarkm max_user, 57455682Smarkm max_group, 57555682Smarkm max_size, 57655682Smarkm max_major, 57755682Smarkm max_minor, 57855682Smarkm max_date); 57955682Smarkm else 58055682Smarkm for(i = 0; i < n_files; i++) 58155682Smarkm print_file(out, 58255682Smarkm flags, 58355682Smarkm &fi[i], 58455682Smarkm max_inode, 58555682Smarkm max_bsize, 58655682Smarkm max_n_link, 58755682Smarkm max_user, 58855682Smarkm max_group, 58955682Smarkm max_size, 59055682Smarkm max_major, 59155682Smarkm max_minor, 59255682Smarkm max_date); 59372445Sassar } else if(DISP_MODE(flags) == LS_DISP_COLUMN || 59472445Sassar DISP_MODE(flags) == LS_DISP_CROSS) { 59572445Sassar int max_len = 0; 59690926Snectar int size_len = 0; 59772445Sassar int num_files = n_files; 59872445Sassar int columns; 59972445Sassar int j; 60072445Sassar for(i = 0; i < n_files; i++) { 60172445Sassar if(fi[i].filename == NULL) { 60272445Sassar num_files--; 60372445Sassar continue; 60472445Sassar } 60572445Sassar if(strlen(fi[i].filename) > max_len) 60672445Sassar max_len = strlen(fi[i].filename); 60790926Snectar if(log10(fi[i].bsize) > size_len) 60890926Snectar size_len = log10(fi[i].bsize); 60972445Sassar } 61090926Snectar if(num_files == 0) 61190926Snectar goto next; 61290926Snectar if(flags & LS_SIZE) { 61390926Snectar columns = 80 / (size_len + 1 + max_len + 1); 61490926Snectar max_len = 80 / columns - size_len - 1; 61590926Snectar } else { 61690926Snectar columns = 80 / (max_len + 1); /* get space between columns */ 61790926Snectar max_len = 80 / columns; 61890926Snectar } 61990926Snectar if(flags & LS_SIZE) 62090926Snectar sec_fprintf2(out, "total %lu\r\n", 62190926Snectar (unsigned long)total_blocks); 62272445Sassar if(DISP_MODE(flags) == LS_DISP_CROSS) { 62372445Sassar for(i = 0, j = 0; i < n_files; i++) { 62472445Sassar if(fi[i].filename == NULL) 62572445Sassar continue; 62690926Snectar if(flags & LS_SIZE) 62790926Snectar sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize, 62890926Snectar max_len, fi[i].filename); 62990926Snectar else 63090926Snectar sec_fprintf2(out, "%-*s", max_len, fi[i].filename); 63172445Sassar j++; 63272445Sassar if(j == columns) { 63372445Sassar sec_fprintf2(out, "\r\n"); 63472445Sassar j = 0; 63572445Sassar } 63672445Sassar } 63772445Sassar if(j > 0) 63890926Snectar sec_fprintf2(out, "\r\n"); 63972445Sassar } else { 64072445Sassar int skip = (num_files + columns - 1) / columns; 64172445Sassar j = 0; 64272445Sassar for(i = 0; i < skip; i++) { 64372445Sassar for(j = i; j < n_files;) { 64472445Sassar while(j < n_files && fi[j].filename == NULL) 64572445Sassar j++; 64690926Snectar if(flags & LS_SIZE) 64790926Snectar sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize, 64890926Snectar max_len, fi[j].filename); 64990926Snectar else 65090926Snectar sec_fprintf2(out, "%-*s", max_len, fi[j].filename); 65172445Sassar j += skip; 65272445Sassar } 65372445Sassar sec_fprintf2(out, "\r\n"); 65472445Sassar } 65572445Sassar } 65672445Sassar } else { 65772445Sassar for(i = 0; i < n_files; i++) { 65872445Sassar if(fi[i].filename == NULL) 65972445Sassar continue; 66072445Sassar sec_fprintf2(out, "%s\r\n", fi[i].filename); 66172445Sassar } 66255682Smarkm } 66390926Snectar next: 66490926Snectar if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) { 66590926Snectar for(i = 0; i < n_files; i++) { 66690926Snectar if(dirs[i]) { 66790926Snectar const char *p = strrchr(files[i], '/'); 66890926Snectar if(p == NULL) 66990926Snectar p = files[i]; 67090926Snectar else 67190926Snectar p++; 67290926Snectar if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) { 67390926Snectar if((flags & LS_SHOW_DIRNAME)) { 67490926Snectar if ((flags & LS_EXTRA_BLANK)) 67590926Snectar sec_fprintf2(out, "\r\n"); 67690926Snectar sec_fprintf2(out, "%s:\r\n", files[i]); 67790926Snectar } 67890926Snectar list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK); 67990926Snectar } 68090926Snectar } 68190926Snectar } 68290926Snectar } 68390926Snectar out: 68472445Sassar for(i = 0; i < n_files; i++) 68572445Sassar free_fileinfo(&fi[i]); 68672445Sassar free(fi); 68790926Snectar if(dirs != NULL) 68890926Snectar free(dirs); 68955682Smarkm} 69055682Smarkm 69155682Smarkmstatic void 69255682Smarkmfree_files (char **files, int n) 69355682Smarkm{ 69455682Smarkm int i; 69555682Smarkm 69655682Smarkm for (i = 0; i < n; ++i) 69755682Smarkm free (files[i]); 69855682Smarkm free (files); 69955682Smarkm} 70055682Smarkm 70190926Snectarstatic int 70290926Snectarhide_file(const char *filename, int flags) 70390926Snectar{ 70490926Snectar if(filename[0] != '.') 70590926Snectar return 0; 70690926Snectar if((flags & LS_IGNORE_DOT)) 70790926Snectar return 1; 70890926Snectar if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) { 70990926Snectar if((flags & LS_SHOW_ALL)) 71090926Snectar return 0; 71190926Snectar else 71290926Snectar return 1; 71390926Snectar } 71490926Snectar return 0; 71590926Snectar} 71690926Snectar 71755682Smarkmstatic void 71855682Smarkmlist_dir(FILE *out, const char *directory, int flags) 71955682Smarkm{ 72055682Smarkm DIR *d = opendir(directory); 72155682Smarkm struct dirent *ent; 72255682Smarkm char **files = NULL; 72355682Smarkm int n_files = 0; 72455682Smarkm 72555682Smarkm if(d == NULL) { 72655682Smarkm sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno)); 72755682Smarkm return; 72855682Smarkm } 72955682Smarkm while((ent = readdir(d)) != NULL) { 73055682Smarkm void *tmp; 73155682Smarkm 73290926Snectar if(hide_file(ent->d_name, flags)) 73390926Snectar continue; 73455682Smarkm tmp = realloc(files, (n_files + 1) * sizeof(*files)); 73555682Smarkm if (tmp == NULL) { 73655682Smarkm sec_fprintf2(out, "%s: out of memory\r\n", directory); 73755682Smarkm free_files (files, n_files); 73855682Smarkm closedir (d); 73955682Smarkm return; 74055682Smarkm } 74155682Smarkm files = tmp; 74255682Smarkm asprintf(&files[n_files], "%s/%s", directory, ent->d_name); 74355682Smarkm if (files[n_files] == NULL) { 74455682Smarkm sec_fprintf2(out, "%s: out of memory\r\n", directory); 74555682Smarkm free_files (files, n_files); 74655682Smarkm closedir (d); 74755682Smarkm return; 74855682Smarkm } 74955682Smarkm ++n_files; 75055682Smarkm } 75155682Smarkm closedir(d); 75290926Snectar list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG); 75355682Smarkm} 75455682Smarkm 75590926Snectarstatic int 75690926Snectarparse_flags(const char *options) 75790926Snectar{ 75890926Snectar#ifdef TEST 75990926Snectar int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN; 76090926Snectar#else 76190926Snectar int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG; 76290926Snectar#endif 76390926Snectar 76490926Snectar const char *p; 76590926Snectar if(options == NULL || *options != '-') 76690926Snectar return flags; 76790926Snectar for(p = options + 1; *p; p++) { 76890926Snectar switch(*p) { 76990926Snectar case '1': 77090926Snectar flags = (flags & ~LS_DISP_MODE); 77190926Snectar break; 77290926Snectar case 'a': 77390926Snectar flags |= LS_SHOW_ALL; 77490926Snectar /*FALLTHROUGH*/ 77590926Snectar case 'A': 77690926Snectar flags &= ~LS_IGNORE_DOT; 77790926Snectar break; 77890926Snectar case 'C': 77990926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN; 78090926Snectar break; 78190926Snectar case 'd': 78290926Snectar flags |= LS_DIRS; 78390926Snectar break; 78490926Snectar case 'f': 78590926Snectar flags = (flags & ~LS_SORT_MODE); 78690926Snectar break; 78790926Snectar case 'F': 78890926Snectar flags |= LS_TYPE; 78990926Snectar break; 79090926Snectar case 'i': 79190926Snectar flags |= LS_INODE; 79290926Snectar break; 79390926Snectar case 'l': 79490926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG; 79590926Snectar break; 79690926Snectar case 'r': 79790926Snectar flags |= LS_SORT_REVERSE; 79890926Snectar break; 79990926Snectar case 'R': 80090926Snectar flags |= LS_RECURSIVE; 80190926Snectar break; 80290926Snectar case 's': 80390926Snectar flags |= LS_SIZE; 80490926Snectar break; 80590926Snectar case 'S': 80690926Snectar flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; 80790926Snectar break; 80890926Snectar case 't': 80990926Snectar flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; 81090926Snectar break; 81190926Snectar case 'x': 81290926Snectar flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS; 81390926Snectar break; 81490926Snectar /* these are a bunch of unimplemented flags from BSD ls */ 81590926Snectar case 'k': /* display sizes in kB */ 81690926Snectar case 'c': /* last change time */ 81790926Snectar case 'L': /* list symlink target */ 81890926Snectar case 'm': /* stream output */ 81990926Snectar case 'o': /* BSD file flags */ 82090926Snectar case 'p': /* display / after directories */ 82190926Snectar case 'q': /* print non-graphic characters */ 82290926Snectar case 'u': /* use last access time */ 82390926Snectar case 'T': /* display complete time */ 82490926Snectar case 'W': /* include whiteouts */ 82590926Snectar break; 82690926Snectar } 82790926Snectar } 82890926Snectar return flags; 82990926Snectar} 83090926Snectar 83155682Smarkmvoid 83255682Smarkmbuiltin_ls(FILE *out, const char *file) 83355682Smarkm{ 83490926Snectar int flags; 83555682Smarkm 83655682Smarkm if(*file == '-') { 83790926Snectar flags = parse_flags(file); 83855682Smarkm file = "."; 83990926Snectar } else 84090926Snectar flags = parse_flags(""); 84190926Snectar 84255682Smarkm list_files(out, &file, 1, flags); 84355682Smarkm sec_fflush(out); 84455682Smarkm} 845