 4b87042b2b
			
		
	
	4b87042b2b
	
	
	
		
			
			git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@11193 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			855 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			855 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
 | |
|  * (Royal Institute of Technology, Stockholm, Sweden). 
 | |
|  * All rights reserved. 
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without 
 | |
|  * modification, are permitted provided that the following conditions 
 | |
|  * are met: 
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright 
 | |
|  *    notice, this list of conditions and the following disclaimer. 
 | |
|  *
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright 
 | |
|  *    notice, this list of conditions and the following disclaimer in the 
 | |
|  *    documentation and/or other materials provided with the distribution. 
 | |
|  *
 | |
|  * 3. Neither the name of KTH nor the names of its contributors may be
 | |
|  *    used to endorse or promote products derived from this software without
 | |
|  *    specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
 | |
|  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
 | |
|  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 | |
|  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
|  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 | |
|  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | |
|  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 | |
| 
 | |
| #ifndef TEST
 | |
| #include "ftpd_locl.h"
 | |
| 
 | |
| RCSID("$Id$");
 | |
| 
 | |
| #else
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <time.h>
 | |
| #include <dirent.h>
 | |
| #include <sys/stat.h>
 | |
| #include <unistd.h>
 | |
| #include <pwd.h>
 | |
| #include <grp.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #define sec_fprintf2 fprintf
 | |
| #define sec_fflush fflush
 | |
| static void list_files(FILE *out, const char **files, int n_files, int flags);
 | |
| static int parse_flags(const char *options);
 | |
| 
 | |
| int
 | |
| main(int argc, char **argv)
 | |
| {
 | |
|     int i = 1;
 | |
|     int flags;
 | |
|     if(argc > 1 && argv[1][0] == '-') {
 | |
| 	flags = parse_flags(argv[1]);
 | |
| 	i = 2;
 | |
|     } else
 | |
| 	flags = parse_flags(NULL);
 | |
| 
 | |
|     list_files(stdout, (const char **)argv + i, argc - i, flags);
 | |
|     return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| struct fileinfo {
 | |
|     struct stat st;
 | |
|     int inode;
 | |
|     int bsize;
 | |
|     char mode[11];
 | |
|     int n_link;
 | |
|     char *user;
 | |
|     char *group;
 | |
|     char *size;
 | |
|     char *major;
 | |
|     char *minor;
 | |
|     char *date;
 | |
|     char *filename;
 | |
|     char *link;
 | |
| };
 | |
| 
 | |
| static void
 | |
| free_fileinfo(struct fileinfo *f)
 | |
| {
 | |
|     free(f->user);
 | |
|     free(f->group);
 | |
|     free(f->size);
 | |
|     free(f->major);
 | |
|     free(f->minor);
 | |
|     free(f->date);
 | |
|     free(f->filename);
 | |
|     free(f->link);
 | |
| }
 | |
| 
 | |
| #define LS_DIRS		(1 << 0)
 | |
| #define LS_IGNORE_DOT	(1 << 1)
 | |
| #define LS_SORT_MODE	(3 << 2)
 | |
| #define SORT_MODE(f) ((f) & LS_SORT_MODE)
 | |
| #define LS_SORT_NAME	(1 << 2)
 | |
| #define LS_SORT_MTIME	(2 << 2)
 | |
| #define LS_SORT_SIZE	(3 << 2)
 | |
| #define LS_SORT_REVERSE	(1 << 4)
 | |
| 
 | |
| #define LS_SIZE		(1 << 5)
 | |
| #define LS_INODE	(1 << 6)
 | |
| #define LS_TYPE		(1 << 7)
 | |
| #define LS_DISP_MODE	(3 << 8)
 | |
| #define DISP_MODE(f) ((f) & LS_DISP_MODE)
 | |
| #define LS_DISP_LONG	(1 << 8)
 | |
| #define LS_DISP_COLUMN	(2 << 8)
 | |
| #define LS_DISP_CROSS	(3 << 8)
 | |
| #define LS_SHOW_ALL	(1 << 10)
 | |
| #define LS_RECURSIVE	(1 << 11)
 | |
| #define LS_EXTRA_BLANK	(1 << 12)
 | |
| #define LS_SHOW_DIRNAME	(1 << 13)
 | |
| #define LS_DIR_FLAG	(1 << 14)	/* these files come via list_dir */
 | |
| 
 | |
| #ifndef S_ISTXT
 | |
| #define S_ISTXT S_ISVTX
 | |
| #endif
 | |
| 
 | |
| #if !defined(_S_IFMT) && defined(S_IFMT)
 | |
| #define _S_IFMT S_IFMT
 | |
| #endif
 | |
| 
 | |
| #ifndef S_ISSOCK
 | |
| #define S_ISSOCK(mode)  (((mode) & _S_IFMT) == S_IFSOCK)
 | |
| #endif
 | |
| 
 | |
| #ifndef S_ISLNK
 | |
| #define S_ISLNK(mode)   (((mode) & _S_IFMT) == S_IFLNK)
 | |
| #endif
 | |
| 
 | |
| static size_t
 | |
| block_convert(size_t blocks)
 | |
| {
 | |
| #ifdef S_BLKSIZE
 | |
|     return blocks * S_BLKSIZE / 1024;
 | |
| #else
 | |
|     return blocks * 512 / 1024;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static void
 | |
| make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
 | |
| {
 | |
|     char buf[128];
 | |
|     int file_type = 0;
 | |
|     struct stat *st = &file->st;
 | |
| 
 | |
|     file->inode = st->st_ino;
 | |
|     file->bsize = block_convert(st->st_blocks);
 | |
| 
 | |
|     if(S_ISDIR(st->st_mode)) {
 | |
| 	file->mode[0] = 'd';
 | |
| 	file_type = '/';
 | |
|     }
 | |
|     else if(S_ISCHR(st->st_mode))
 | |
| 	file->mode[0] = 'c';
 | |
|     else if(S_ISBLK(st->st_mode))
 | |
| 	file->mode[0] = 'b';
 | |
|     else if(S_ISREG(st->st_mode)) {
 | |
| 	file->mode[0] = '-';
 | |
| 	if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
 | |
| 	    file_type = '*';
 | |
|     }
 | |
|     else if(S_ISFIFO(st->st_mode)) {
 | |
| 	file->mode[0] = 'p';
 | |
| 	file_type = '|';
 | |
|     }
 | |
|     else if(S_ISLNK(st->st_mode)) {
 | |
| 	file->mode[0] = 'l';
 | |
| 	file_type = '@';
 | |
|     }
 | |
|     else if(S_ISSOCK(st->st_mode)) {
 | |
| 	file->mode[0] = 's';
 | |
| 	file_type = '=';
 | |
|     }
 | |
| #ifdef S_ISWHT
 | |
|     else if(S_ISWHT(st->st_mode)) {
 | |
| 	file->mode[0] = 'w';
 | |
| 	file_type = '%';
 | |
|     }
 | |
| #endif
 | |
|     else 
 | |
| 	file->mode[0] = '?';
 | |
|     {
 | |
| 	char *x[] = { "---", "--x", "-w-", "-wx", 
 | |
| 		      "r--", "r-x", "rw-", "rwx" };
 | |
| 	strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
 | |
| 	strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
 | |
| 	strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
 | |
| 	if((st->st_mode & S_ISUID)) {
 | |
| 	    if((st->st_mode & S_IXUSR))
 | |
| 		file->mode[3] = 's';
 | |
| 	    else
 | |
| 		file->mode[3] = 'S';
 | |
| 	}
 | |
| 	if((st->st_mode & S_ISGID)) {
 | |
| 	    if((st->st_mode & S_IXGRP))
 | |
| 		file->mode[6] = 's';
 | |
| 	    else
 | |
| 		file->mode[6] = 'S';
 | |
| 	}
 | |
| 	if((st->st_mode & S_ISTXT)) {
 | |
| 	    if((st->st_mode & S_IXOTH))
 | |
| 		file->mode[9] = 't';
 | |
| 	    else
 | |
| 		file->mode[9] = 'T';
 | |
| 	}
 | |
|     }
 | |
|     file->n_link = st->st_nlink;
 | |
|     {
 | |
| 	struct passwd *pwd;
 | |
| 	pwd = getpwuid(st->st_uid);
 | |
| 	if(pwd == NULL)
 | |
| 	    asprintf(&file->user, "%u", (unsigned)st->st_uid);
 | |
| 	else
 | |
| 	    file->user = strdup(pwd->pw_name);
 | |
|     }
 | |
|     {
 | |
| 	struct group *grp;
 | |
| 	grp = getgrgid(st->st_gid);
 | |
| 	if(grp == NULL)
 | |
| 	    asprintf(&file->group, "%u", (unsigned)st->st_gid);
 | |
| 	else
 | |
| 	    file->group = strdup(grp->gr_name);
 | |
|     }
 | |
|     
 | |
|     if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
 | |
| #if defined(major) && defined(minor)
 | |
| 	asprintf(&file->major, "%u", (unsigned)major(st->st_rdev));
 | |
| 	asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev));
 | |
| #else
 | |
| 	/* Don't want to use the DDI/DKI crap. */
 | |
| 	asprintf(&file->major, "%u", (unsigned)st->st_rdev);
 | |
| 	asprintf(&file->minor, "%u", 0);
 | |
| #endif
 | |
|     } else
 | |
| 	asprintf(&file->size, "%lu", (unsigned long)st->st_size);
 | |
| 
 | |
|     {
 | |
| 	time_t t = time(NULL);
 | |
| 	time_t mtime = st->st_mtime;
 | |
| 	struct tm *tm = localtime(&mtime);
 | |
| 	if((t - mtime > 6*30*24*60*60) ||
 | |
| 	   (mtime - t > 6*30*24*60*60))
 | |
| 	    strftime(buf, sizeof(buf), "%b %e  %Y", tm);
 | |
| 	else
 | |
| 	    strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
 | |
| 	file->date = strdup(buf);
 | |
|     }
 | |
|     {
 | |
| 	const char *p = strrchr(filename, '/');
 | |
| 	if(p)
 | |
| 	    p++;
 | |
| 	else
 | |
| 	    p = filename;
 | |
| 	if((flags & LS_TYPE) && file_type != 0)
 | |
| 	    asprintf(&file->filename, "%s%c", p, file_type);
 | |
| 	else
 | |
| 	    file->filename = strdup(p);
 | |
|     }
 | |
|     if(S_ISLNK(st->st_mode)) {
 | |
| 	int n;
 | |
| 	n = readlink((char *)filename, buf, sizeof(buf));
 | |
| 	if(n >= 0) {
 | |
| 	    buf[n] = '\0';
 | |
| 	    file->link = strdup(buf);
 | |
| 	} else
 | |
| 	    sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_file(FILE *out,
 | |
| 	   int flags,
 | |
| 	   struct fileinfo *f,
 | |
| 	   int max_inode,
 | |
| 	   int max_bsize,
 | |
| 	   int max_n_link,
 | |
| 	   int max_user,
 | |
| 	   int max_group,
 | |
| 	   int max_size,
 | |
| 	   int max_major,
 | |
| 	   int max_minor,
 | |
| 	   int max_date)
 | |
| {
 | |
|     if(f->filename == NULL)
 | |
| 	return;
 | |
| 
 | |
|     if(flags & LS_INODE) {
 | |
| 	sec_fprintf2(out, "%*d", max_inode, f->inode);
 | |
| 	sec_fprintf2(out, "  ");
 | |
|     }
 | |
|     if(flags & LS_SIZE) {
 | |
| 	sec_fprintf2(out, "%*d", max_bsize, f->bsize);
 | |
| 	sec_fprintf2(out, "  ");
 | |
|     }
 | |
|     sec_fprintf2(out, "%s", f->mode);
 | |
|     sec_fprintf2(out, "  ");
 | |
|     sec_fprintf2(out, "%*d", max_n_link, f->n_link);
 | |
|     sec_fprintf2(out, " ");
 | |
|     sec_fprintf2(out, "%-*s", max_user, f->user);
 | |
|     sec_fprintf2(out, "  ");
 | |
|     sec_fprintf2(out, "%-*s", max_group, f->group);
 | |
|     sec_fprintf2(out, "  ");
 | |
|     if(f->major != NULL && f->minor != NULL)
 | |
| 	sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
 | |
|     else
 | |
| 	sec_fprintf2(out, "%*s", max_size, f->size);
 | |
|     sec_fprintf2(out, " ");
 | |
|     sec_fprintf2(out, "%*s", max_date, f->date);
 | |
|     sec_fprintf2(out, " ");
 | |
|     sec_fprintf2(out, "%s", f->filename);
 | |
|     if(f->link)
 | |
| 	sec_fprintf2(out, " -> %s", f->link);
 | |
|     sec_fprintf2(out, "\r\n");
 | |
| }
 | |
| 
 | |
| static int
 | |
| compare_filename(struct fileinfo *a, struct fileinfo *b)
 | |
| {
 | |
|     if(a->filename == NULL)
 | |
| 	return 1;
 | |
|     if(b->filename == NULL)
 | |
| 	return -1;
 | |
|     return strcmp(a->filename, b->filename);
 | |
| }
 | |
| 
 | |
| static int
 | |
| compare_mtime(struct fileinfo *a, struct fileinfo *b)
 | |
| {
 | |
|     if(a->filename == NULL)
 | |
| 	return 1;
 | |
|     if(b->filename == NULL)
 | |
| 	return -1;
 | |
|     return b->st.st_mtime - a->st.st_mtime;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compare_size(struct fileinfo *a, struct fileinfo *b)
 | |
| {
 | |
|     if(a->filename == NULL)
 | |
| 	return 1;
 | |
|     if(b->filename == NULL)
 | |
| 	return -1;
 | |
|     return b->st.st_size - a->st.st_size;
 | |
| }
 | |
| 
 | |
| static int list_dir(FILE*, const char*, int);
 | |
| 
 | |
| static int
 | |
| log10(int num)
 | |
| {
 | |
|     int i = 1;
 | |
|     while(num > 10) {
 | |
| 	i++;
 | |
| 	num /= 10;
 | |
|     }
 | |
|     return i;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Operate as lstat but fake up entries for AFS mount points so we don't
 | |
|  * have to fetch them.
 | |
|  */
 | |
| 
 | |
| #ifdef KRB4
 | |
| static int do_the_afs_dance = 1;
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| lstat_file (const char *file, struct stat *sb)
 | |
| {
 | |
| #ifdef KRB4
 | |
|     if (do_the_afs_dance &&
 | |
| 	k_hasafs() 
 | |
| 	&& strcmp(file, ".")
 | |
| 	&& strcmp(file, "..")
 | |
| 	&& strcmp(file, "/"))
 | |
|     {
 | |
| 	struct ViceIoctl    a_params;
 | |
| 	char               *dir, *last;
 | |
| 	char               *path_bkp;
 | |
| 	static ino_t	   ino_counter = 0, ino_last = 0;
 | |
| 	int		   ret;
 | |
| 	const int	   maxsize = 2048;
 | |
| 	
 | |
| 	path_bkp = strdup (file);
 | |
| 	if (path_bkp == NULL)
 | |
| 	    return -1;
 | |
| 	
 | |
| 	a_params.out = malloc (maxsize);
 | |
| 	if (a_params.out == NULL) { 
 | |
| 	    free (path_bkp);
 | |
| 	    return -1;
 | |
| 	}
 | |
| 	
 | |
| 	/* If path contains more than the filename alone - split it */
 | |
| 	
 | |
| 	last = strrchr (path_bkp, '/');
 | |
| 	if (last != NULL) {
 | |
| 	    if(last[1] == '\0')
 | |
| 		/* if path ended in /, replace with `.' */
 | |
| 		a_params.in = ".";
 | |
| 	    else
 | |
| 		a_params.in = last + 1;
 | |
| 	    while(last > path_bkp && *--last == '/');
 | |
| 	    if(*last != '/' || last != path_bkp) {
 | |
| 		*++last = '\0';
 | |
| 		dir = path_bkp;
 | |
| 	    } else
 | |
| 		/* we got to the start, so this must be the root dir */
 | |
| 		dir = "/";
 | |
| 	} else {
 | |
| 	    /* file is relative to cdir */
 | |
| 	    dir = ".";
 | |
| 	    a_params.in = path_bkp;
 | |
| 	}
 | |
| 	
 | |
| 	a_params.in_size  = strlen (a_params.in) + 1;
 | |
| 	a_params.out_size = maxsize;
 | |
| 	
 | |
| 	ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
 | |
| 	free (a_params.out);
 | |
| 	if (ret < 0) {
 | |
| 	    free (path_bkp);
 | |
| 
 | |
| 	    if (errno != EINVAL)
 | |
| 		return ret;
 | |
| 	    else
 | |
| 		/* if we get EINVAL this is probably not a mountpoint */
 | |
| 		return lstat (file, sb);
 | |
| 	}
 | |
| 
 | |
| 	/* 
 | |
| 	 * wow this was a mountpoint, lets cook the struct stat
 | |
| 	 * use . as a prototype
 | |
| 	 */
 | |
| 
 | |
| 	ret = lstat (dir, sb);
 | |
| 	free (path_bkp);
 | |
| 	if (ret < 0)
 | |
| 	    return ret;
 | |
| 
 | |
| 	if (ino_last == sb->st_ino)
 | |
| 	    ino_counter++;
 | |
| 	else {
 | |
| 	    ino_last    = sb->st_ino;
 | |
| 	    ino_counter = 0;
 | |
| 	}
 | |
| 	sb->st_ino += ino_counter;
 | |
| 	sb->st_nlink = 3;
 | |
| 
 | |
| 	return 0;
 | |
|     }
 | |
| #endif /* KRB4 */
 | |
|     return lstat (file, sb);
 | |
| }
 | |
| 
 | |
| #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
 | |
| 				((X)[1] == '.' && (X)[2] == '\0')))
 | |
| 
 | |
| static int
 | |
| list_files(FILE *out, const char **files, int n_files, int flags)
 | |
| {
 | |
|     struct fileinfo *fi;
 | |
|     int i;
 | |
|     int *dirs = NULL;
 | |
|     size_t total_blocks = 0;
 | |
|     int n_print = 0;
 | |
|     int ret = 0;
 | |
| 
 | |
|     if(n_files == 0)
 | |
| 	return 0;
 | |
| 
 | |
|     if(n_files > 1)
 | |
| 	flags |= LS_SHOW_DIRNAME;
 | |
| 
 | |
|     fi = calloc(n_files, sizeof(*fi));
 | |
|     if (fi == NULL) {
 | |
| 	syslog(LOG_ERR, "out of memory");
 | |
| 	return -1;
 | |
|     }
 | |
|     for(i = 0; i < n_files; i++) {
 | |
| 	if(lstat_file(files[i], &fi[i].st) < 0) {
 | |
| 	    sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
 | |
| 	    fi[i].filename = NULL;
 | |
| 	} else {
 | |
| 	    int include_in_list = 1;
 | |
| 	    total_blocks += block_convert(fi[i].st.st_blocks);
 | |
| 	    if(S_ISDIR(fi[i].st.st_mode)) {
 | |
| 		if(dirs == NULL)
 | |
| 		    dirs = calloc(n_files, sizeof(*dirs));
 | |
| 		if(dirs == NULL) {
 | |
| 		    syslog(LOG_ERR, "%s: %m", files[i]);
 | |
| 		    ret = -1;
 | |
| 		    goto out;
 | |
| 		}
 | |
| 		dirs[i] = 1;
 | |
| 		if((flags & LS_DIRS) == 0)
 | |
| 		    include_in_list = 0;
 | |
| 	    }
 | |
| 	    if(include_in_list) {
 | |
| 		make_fileinfo(out, files[i], &fi[i], flags);
 | |
| 		n_print++;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     switch(SORT_MODE(flags)) {
 | |
|     case LS_SORT_NAME:
 | |
| 	qsort(fi, n_files, sizeof(*fi), 
 | |
| 	      (int (*)(const void*, const void*))compare_filename);
 | |
| 	break;
 | |
|     case LS_SORT_MTIME:
 | |
| 	qsort(fi, n_files, sizeof(*fi), 
 | |
| 	      (int (*)(const void*, const void*))compare_mtime);
 | |
| 	break;
 | |
|     case LS_SORT_SIZE:
 | |
| 	qsort(fi, n_files, sizeof(*fi), 
 | |
| 	      (int (*)(const void*, const void*))compare_size);
 | |
| 	break;
 | |
|     }
 | |
|     if(DISP_MODE(flags) == LS_DISP_LONG) {
 | |
| 	int max_inode = 0;
 | |
| 	int max_bsize = 0;
 | |
| 	int max_n_link = 0;
 | |
| 	int max_user = 0;
 | |
| 	int max_group = 0;
 | |
| 	int max_size = 0;
 | |
| 	int max_major = 0;
 | |
| 	int max_minor = 0;
 | |
| 	int max_date = 0;
 | |
| 	for(i = 0; i < n_files; i++) {
 | |
| 	    if(fi[i].filename == NULL)
 | |
| 		continue;
 | |
| 	    if(fi[i].inode > max_inode)
 | |
| 		max_inode = fi[i].inode;
 | |
| 	    if(fi[i].bsize > max_bsize)
 | |
| 		max_bsize = fi[i].bsize;
 | |
| 	    if(fi[i].n_link > max_n_link)
 | |
| 		max_n_link = fi[i].n_link;
 | |
| 	    if(strlen(fi[i].user) > max_user)
 | |
| 		max_user = strlen(fi[i].user);
 | |
| 	    if(strlen(fi[i].group) > max_group)
 | |
| 		max_group = strlen(fi[i].group);
 | |
| 	    if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
 | |
| 		max_major = strlen(fi[i].major);
 | |
| 	    if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
 | |
| 		max_minor = strlen(fi[i].minor);
 | |
| 	    if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
 | |
| 		max_size = strlen(fi[i].size);
 | |
| 	    if(strlen(fi[i].date) > max_date)
 | |
| 		max_date = strlen(fi[i].date);
 | |
| 	}
 | |
| 	if(max_size < max_major + max_minor + 2)
 | |
| 	    max_size = max_major + max_minor + 2;
 | |
| 	else if(max_size - max_minor - 2 > max_major)
 | |
| 	    max_major = max_size - max_minor - 2;
 | |
| 	max_inode = log10(max_inode);
 | |
| 	max_bsize = log10(max_bsize);
 | |
| 	max_n_link = log10(max_n_link);
 | |
| 	
 | |
| 	if(n_print > 0)
 | |
| 	    sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
 | |
| 	if(flags & LS_SORT_REVERSE)
 | |
| 	    for(i = n_files - 1; i >= 0; i--)
 | |
| 		print_file(out,
 | |
| 			   flags,
 | |
| 			   &fi[i],
 | |
| 			   max_inode,
 | |
| 			   max_bsize,
 | |
| 			   max_n_link,
 | |
| 			   max_user,
 | |
| 			   max_group,
 | |
| 			   max_size,
 | |
| 			   max_major,
 | |
| 			   max_minor,
 | |
| 			   max_date);
 | |
| 	else
 | |
| 	    for(i = 0; i < n_files; i++)
 | |
| 		print_file(out,
 | |
| 			   flags,
 | |
| 			   &fi[i],
 | |
| 			   max_inode,
 | |
| 			   max_bsize,
 | |
| 			   max_n_link,
 | |
| 			   max_user,
 | |
| 			   max_group,
 | |
| 			   max_size,
 | |
| 			   max_major,
 | |
| 			   max_minor,
 | |
| 			   max_date);
 | |
|     } else if(DISP_MODE(flags) == LS_DISP_COLUMN || 
 | |
| 	      DISP_MODE(flags) == LS_DISP_CROSS) {
 | |
| 	int max_len = 0;
 | |
| 	int size_len = 0;
 | |
| 	int num_files = n_files;
 | |
| 	int columns;
 | |
| 	int j;
 | |
| 	for(i = 0; i < n_files; i++) {
 | |
| 	    if(fi[i].filename == NULL) {
 | |
| 		num_files--;
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    if(strlen(fi[i].filename) > max_len)
 | |
| 		max_len = strlen(fi[i].filename);
 | |
| 	    if(log10(fi[i].bsize) > size_len)
 | |
| 		size_len = log10(fi[i].bsize);
 | |
| 	}
 | |
| 	if(num_files == 0)
 | |
| 	    goto next;
 | |
| 	if(flags & LS_SIZE) {
 | |
| 	    columns = 80 / (size_len + 1 + max_len + 1);
 | |
| 	    max_len = 80 / columns - size_len - 1;
 | |
| 	} else {
 | |
| 	    columns = 80 / (max_len + 1); /* get space between columns */
 | |
| 	    max_len = 80 / columns;
 | |
| 	}
 | |
| 	if(flags & LS_SIZE)
 | |
| 	    sec_fprintf2(out, "total %lu\r\n", 
 | |
| 			 (unsigned long)total_blocks);
 | |
| 	if(DISP_MODE(flags) == LS_DISP_CROSS) {
 | |
| 	    for(i = 0, j = 0; i < n_files; i++) {
 | |
| 		if(fi[i].filename == NULL)
 | |
| 		    continue;
 | |
| 		if(flags & LS_SIZE)
 | |
| 		    sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize, 
 | |
| 				 max_len, fi[i].filename);
 | |
| 		else
 | |
| 		    sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
 | |
| 		j++;
 | |
| 		if(j == columns) {
 | |
| 		    sec_fprintf2(out, "\r\n");
 | |
| 		    j = 0;
 | |
| 		}
 | |
| 	    }
 | |
| 	    if(j > 0)
 | |
| 		sec_fprintf2(out, "\r\n");
 | |
| 	} else {
 | |
| 	    int skip = (num_files + columns - 1) / columns;
 | |
| 	    j = 0;
 | |
| 	    for(i = 0; i < skip; i++) {
 | |
| 		for(j = i; j < n_files;) {
 | |
| 		    while(j < n_files && fi[j].filename == NULL)
 | |
| 			j++;
 | |
| 		    if(flags & LS_SIZE)
 | |
| 			sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize, 
 | |
| 				     max_len, fi[j].filename);
 | |
| 		    else
 | |
| 			sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
 | |
| 		    j += skip;
 | |
| 		}
 | |
| 		sec_fprintf2(out, "\r\n");
 | |
| 	    }
 | |
| 	}
 | |
|     } else {
 | |
| 	for(i = 0; i < n_files; i++) {
 | |
| 	    if(fi[i].filename == NULL)
 | |
| 		continue;
 | |
| 	    sec_fprintf2(out, "%s\r\n", fi[i].filename);
 | |
| 	}
 | |
|     }
 | |
|  next:
 | |
|     if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
 | |
| 	for(i = 0; i < n_files; i++) {
 | |
| 	    if(dirs[i]) {
 | |
| 		const char *p = strrchr(files[i], '/');
 | |
| 		if(p == NULL)
 | |
| 		    p = files[i];
 | |
| 		else 
 | |
| 		    p++;
 | |
| 		if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
 | |
| 		    if((flags & LS_SHOW_DIRNAME)) {
 | |
| 			if ((flags & LS_EXTRA_BLANK))
 | |
| 			    sec_fprintf2(out, "\r\n");
 | |
| 			sec_fprintf2(out, "%s:\r\n", files[i]);
 | |
| 		    }
 | |
| 		    list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|  out:
 | |
|     for(i = 0; i < n_files; i++)
 | |
| 	free_fileinfo(&fi[i]);
 | |
|     free(fi);
 | |
|     if(dirs != NULL)
 | |
| 	free(dirs);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| free_files (char **files, int n)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n; ++i)
 | |
| 	free (files[i]);
 | |
|     free (files);
 | |
| }
 | |
| 
 | |
| static int
 | |
| hide_file(const char *filename, int flags)
 | |
| {
 | |
|     if(filename[0] != '.')
 | |
| 	return 0;
 | |
|     if((flags & LS_IGNORE_DOT))
 | |
| 	return 1;
 | |
|     if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
 | |
| 	if((flags & LS_SHOW_ALL))
 | |
| 	    return 0;
 | |
| 	else
 | |
| 	    return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| list_dir(FILE *out, const char *directory, int flags)
 | |
| {
 | |
|     DIR *d = opendir(directory);
 | |
|     struct dirent *ent;
 | |
|     char **files = NULL;
 | |
|     int n_files = 0;
 | |
| 
 | |
|     if(d == NULL) {
 | |
| 	syslog(LOG_ERR, "%s: %m", directory);
 | |
| 	return -1;
 | |
|     }
 | |
|     while((ent = readdir(d)) != NULL) {
 | |
| 	void *tmp;
 | |
| 
 | |
| 	if(hide_file(ent->d_name, flags))
 | |
| 	    continue;
 | |
| 	tmp = realloc(files, (n_files + 1) * sizeof(*files));
 | |
| 	if (tmp == NULL) {
 | |
| 	    syslog(LOG_ERR, "%s: out of memory", directory);
 | |
| 	    free_files (files, n_files);
 | |
| 	    closedir (d);
 | |
| 	    return -1;
 | |
| 	}
 | |
| 	files = tmp;
 | |
| 	asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
 | |
| 	if (files[n_files] == NULL) {
 | |
| 	    syslog(LOG_ERR, "%s: out of memory", directory);
 | |
| 	    free_files (files, n_files);
 | |
| 	    closedir (d);
 | |
| 	    return -1;
 | |
| 	}
 | |
| 	++n_files;
 | |
|     }
 | |
|     closedir(d);
 | |
|     return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
 | |
| }
 | |
| 
 | |
| static int
 | |
| parse_flags(const char *options)
 | |
| {
 | |
| #ifdef TEST
 | |
|     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
 | |
| #else
 | |
|     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
 | |
| #endif
 | |
| 
 | |
|     const char *p;
 | |
|     if(options == NULL || *options != '-')
 | |
| 	return flags;
 | |
|     for(p = options + 1; *p; p++) {
 | |
| 	switch(*p) {
 | |
| 	case '1':
 | |
| 	    flags = (flags & ~LS_DISP_MODE);
 | |
| 	    break;
 | |
| 	case 'a':
 | |
| 	    flags |= LS_SHOW_ALL;
 | |
| 	    /*FALLTHROUGH*/
 | |
| 	case 'A':
 | |
| 	    flags &= ~LS_IGNORE_DOT;
 | |
| 	    break;
 | |
| 	case 'C':
 | |
| 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
 | |
| 	    break;
 | |
| 	case 'd':
 | |
| 	    flags |= LS_DIRS;
 | |
| 	    break;
 | |
| 	case 'f':
 | |
| 	    flags = (flags & ~LS_SORT_MODE);
 | |
| 	    break;
 | |
| 	case 'F':
 | |
| 	    flags |= LS_TYPE;
 | |
| 	    break;
 | |
| 	case 'i':
 | |
| 	    flags |= LS_INODE;
 | |
| 	    break;
 | |
| 	case 'l':
 | |
| 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
 | |
| 	    break;
 | |
| 	case 'r':
 | |
| 	    flags |= LS_SORT_REVERSE;
 | |
| 	    break;
 | |
| 	case 'R':
 | |
| 	    flags |= LS_RECURSIVE;
 | |
| 	    break;
 | |
| 	case 's':
 | |
| 	    flags |= LS_SIZE;
 | |
| 	    break;
 | |
| 	case 'S':
 | |
| 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
 | |
| 	    break;
 | |
| 	case 't':
 | |
| 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
 | |
| 	    break;
 | |
| 	case 'x':
 | |
| 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
 | |
| 	    break;
 | |
| 	    /* these are a bunch of unimplemented flags from BSD ls */
 | |
| 	case 'k': /* display sizes in kB */
 | |
| 	case 'c': /* last change time */
 | |
| 	case 'L': /* list symlink target */
 | |
| 	case 'm': /* stream output */
 | |
| 	case 'o': /* BSD file flags */
 | |
| 	case 'p': /* display / after directories */
 | |
| 	case 'q': /* print non-graphic characters */
 | |
| 	case 'u': /* use last access time */
 | |
| 	case 'T': /* display complete time */
 | |
| 	case 'W': /* include whiteouts */
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
|     return flags;
 | |
| }
 | |
| 
 | |
| int
 | |
| builtin_ls(FILE *out, const char *file)
 | |
| {
 | |
|     int flags;
 | |
|     int ret;
 | |
| 
 | |
|     if(*file == '-') {
 | |
| 	flags = parse_flags(file);
 | |
| 	file = ".";
 | |
|     } else
 | |
| 	flags = parse_flags("");
 | |
| 
 | |
|     ret = list_files(out, &file, 1, flags);
 | |
|     sec_fflush(out);
 | |
|     return ret;
 | |
| }
 |