xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ls.c (revision 8373020d34ceb1ac55d8f43333c1ca3680185b39)
1b528cefcSMark Murray /*
28373020dSJacques Vidrine  * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of KTH nor the names of its contributors may be
18b528cefcSMark Murray  *    used to endorse or promote products derived from this software without
19b528cefcSMark Murray  *    specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22b528cefcSMark Murray  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24b528cefcSMark Murray  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25b528cefcSMark Murray  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26b528cefcSMark Murray  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27b528cefcSMark Murray  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28b528cefcSMark Murray  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29b528cefcSMark Murray  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30b528cefcSMark Murray  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31b528cefcSMark Murray  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32b528cefcSMark Murray 
335e9cd1aeSAssar Westerlund #ifndef TEST
34b528cefcSMark Murray #include "ftpd_locl.h"
35b528cefcSMark Murray 
368373020dSJacques Vidrine RCSID("$Id: ls.c,v 1.25 2002/08/22 08:31:03 joda Exp $");
375e9cd1aeSAssar Westerlund 
385e9cd1aeSAssar Westerlund #else
395e9cd1aeSAssar Westerlund #include <stdio.h>
405e9cd1aeSAssar Westerlund #include <string.h>
415e9cd1aeSAssar Westerlund #include <stdlib.h>
425e9cd1aeSAssar Westerlund #include <time.h>
435e9cd1aeSAssar Westerlund #include <dirent.h>
445e9cd1aeSAssar Westerlund #include <sys/stat.h>
455e9cd1aeSAssar Westerlund #include <unistd.h>
465e9cd1aeSAssar Westerlund #include <pwd.h>
475e9cd1aeSAssar Westerlund #include <grp.h>
485e9cd1aeSAssar Westerlund #include <errno.h>
495e9cd1aeSAssar Westerlund 
505e9cd1aeSAssar Westerlund #define sec_fprintf2 fprintf
515e9cd1aeSAssar Westerlund #define sec_fflush fflush
524137ff4cSJacques Vidrine static void list_files(FILE *out, const char **files, int n_files, int flags);
534137ff4cSJacques Vidrine static int parse_flags(const char *options);
544137ff4cSJacques Vidrine 
555e9cd1aeSAssar Westerlund int
565e9cd1aeSAssar Westerlund main(int argc, char **argv)
575e9cd1aeSAssar Westerlund {
584137ff4cSJacques Vidrine     int i = 1;
594137ff4cSJacques Vidrine     int flags;
604137ff4cSJacques Vidrine     if(argc > 1 && argv[1][0] == '-') {
614137ff4cSJacques Vidrine 	flags = parse_flags(argv[1]);
624137ff4cSJacques Vidrine 	i = 2;
634137ff4cSJacques Vidrine     } else
644137ff4cSJacques Vidrine 	flags = parse_flags(NULL);
654137ff4cSJacques Vidrine 
664137ff4cSJacques Vidrine     list_files(stdout, (const char **)argv + i, argc - i, flags);
675e9cd1aeSAssar Westerlund     return 0;
685e9cd1aeSAssar Westerlund }
695e9cd1aeSAssar Westerlund #endif
70b528cefcSMark Murray 
71b528cefcSMark Murray struct fileinfo {
72b528cefcSMark Murray     struct stat st;
73b528cefcSMark Murray     int inode;
74b528cefcSMark Murray     int bsize;
75b528cefcSMark Murray     char mode[11];
76b528cefcSMark Murray     int n_link;
77b528cefcSMark Murray     char *user;
78b528cefcSMark Murray     char *group;
79b528cefcSMark Murray     char *size;
80b528cefcSMark Murray     char *major;
81b528cefcSMark Murray     char *minor;
82b528cefcSMark Murray     char *date;
83b528cefcSMark Murray     char *filename;
84b528cefcSMark Murray     char *link;
85b528cefcSMark Murray };
86b528cefcSMark Murray 
87b528cefcSMark Murray static void
88b528cefcSMark Murray free_fileinfo(struct fileinfo *f)
89b528cefcSMark Murray {
90b528cefcSMark Murray     free(f->user);
91b528cefcSMark Murray     free(f->group);
92b528cefcSMark Murray     free(f->size);
93b528cefcSMark Murray     free(f->major);
94b528cefcSMark Murray     free(f->minor);
95b528cefcSMark Murray     free(f->date);
96b528cefcSMark Murray     free(f->filename);
97b528cefcSMark Murray     free(f->link);
98b528cefcSMark Murray }
99b528cefcSMark Murray 
1005e9cd1aeSAssar Westerlund #define LS_DIRS		(1 << 0)
1015e9cd1aeSAssar Westerlund #define LS_IGNORE_DOT	(1 << 1)
1025e9cd1aeSAssar Westerlund #define LS_SORT_MODE	(3 << 2)
103b528cefcSMark Murray #define SORT_MODE(f) ((f) & LS_SORT_MODE)
1045e9cd1aeSAssar Westerlund #define LS_SORT_NAME	(1 << 2)
1055e9cd1aeSAssar Westerlund #define LS_SORT_MTIME	(2 << 2)
1065e9cd1aeSAssar Westerlund #define LS_SORT_SIZE	(3 << 2)
1075e9cd1aeSAssar Westerlund #define LS_SORT_REVERSE	(1 << 4)
108b528cefcSMark Murray 
1095e9cd1aeSAssar Westerlund #define LS_SIZE		(1 << 5)
1105e9cd1aeSAssar Westerlund #define LS_INODE	(1 << 6)
1115e9cd1aeSAssar Westerlund #define LS_TYPE		(1 << 7)
1125e9cd1aeSAssar Westerlund #define LS_DISP_MODE	(3 << 8)
1135e9cd1aeSAssar Westerlund #define DISP_MODE(f) ((f) & LS_DISP_MODE)
1145e9cd1aeSAssar Westerlund #define LS_DISP_LONG	(1 << 8)
1155e9cd1aeSAssar Westerlund #define LS_DISP_COLUMN	(2 << 8)
1165e9cd1aeSAssar Westerlund #define LS_DISP_CROSS	(3 << 8)
1174137ff4cSJacques Vidrine #define LS_SHOW_ALL	(1 << 10)
1184137ff4cSJacques Vidrine #define LS_RECURSIVE	(1 << 11)
1194137ff4cSJacques Vidrine #define LS_EXTRA_BLANK	(1 << 12)
1204137ff4cSJacques Vidrine #define LS_SHOW_DIRNAME	(1 << 13)
1214137ff4cSJacques Vidrine #define LS_DIR_FLAG	(1 << 14)	/* these files come via list_dir */
122b528cefcSMark Murray 
123b528cefcSMark Murray #ifndef S_ISTXT
124b528cefcSMark Murray #define S_ISTXT S_ISVTX
125b528cefcSMark Murray #endif
126b528cefcSMark Murray 
1278373020dSJacques Vidrine #if !defined(_S_IFMT) && defined(S_IFMT)
1288373020dSJacques Vidrine #define _S_IFMT S_IFMT
1298373020dSJacques Vidrine #endif
1308373020dSJacques Vidrine 
131b528cefcSMark Murray #ifndef S_ISSOCK
132b528cefcSMark Murray #define S_ISSOCK(mode)  (((mode) & _S_IFMT) == S_IFSOCK)
133b528cefcSMark Murray #endif
134b528cefcSMark Murray 
135b528cefcSMark Murray #ifndef S_ISLNK
136b528cefcSMark Murray #define S_ISLNK(mode)   (((mode) & _S_IFMT) == S_IFLNK)
137b528cefcSMark Murray #endif
138b528cefcSMark Murray 
1394137ff4cSJacques Vidrine static size_t
1404137ff4cSJacques Vidrine block_convert(size_t blocks)
1414137ff4cSJacques Vidrine {
1424137ff4cSJacques Vidrine #ifdef S_BLKSIZE
1434137ff4cSJacques Vidrine     return blocks * S_BLKSIZE / 1024;
1444137ff4cSJacques Vidrine #else
1454137ff4cSJacques Vidrine     return blocks * 512 / 1024;
1464137ff4cSJacques Vidrine #endif
1474137ff4cSJacques Vidrine }
1484137ff4cSJacques Vidrine 
149b528cefcSMark Murray static void
1504137ff4cSJacques Vidrine make_fileinfo(FILE *out, const char *filename, struct fileinfo *file, int flags)
151b528cefcSMark Murray {
152b528cefcSMark Murray     char buf[128];
1535e9cd1aeSAssar Westerlund     int file_type = 0;
154b528cefcSMark Murray     struct stat *st = &file->st;
155b528cefcSMark Murray 
156b528cefcSMark Murray     file->inode = st->st_ino;
1574137ff4cSJacques Vidrine     file->bsize = block_convert(st->st_blocks);
158b528cefcSMark Murray 
1595e9cd1aeSAssar Westerlund     if(S_ISDIR(st->st_mode)) {
160b528cefcSMark Murray 	file->mode[0] = 'd';
1615e9cd1aeSAssar Westerlund 	file_type = '/';
1625e9cd1aeSAssar Westerlund     }
163b528cefcSMark Murray     else if(S_ISCHR(st->st_mode))
164b528cefcSMark Murray 	file->mode[0] = 'c';
165b528cefcSMark Murray     else if(S_ISBLK(st->st_mode))
166b528cefcSMark Murray 	file->mode[0] = 'b';
1675e9cd1aeSAssar Westerlund     else if(S_ISREG(st->st_mode)) {
168b528cefcSMark Murray 	file->mode[0] = '-';
1695e9cd1aeSAssar Westerlund 	if(st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
1705e9cd1aeSAssar Westerlund 	    file_type = '*';
1715e9cd1aeSAssar Westerlund     }
1725e9cd1aeSAssar Westerlund     else if(S_ISFIFO(st->st_mode)) {
173b528cefcSMark Murray 	file->mode[0] = 'p';
1745e9cd1aeSAssar Westerlund 	file_type = '|';
1755e9cd1aeSAssar Westerlund     }
1765e9cd1aeSAssar Westerlund     else if(S_ISLNK(st->st_mode)) {
177b528cefcSMark Murray 	file->mode[0] = 'l';
1785e9cd1aeSAssar Westerlund 	file_type = '@';
1795e9cd1aeSAssar Westerlund     }
1805e9cd1aeSAssar Westerlund     else if(S_ISSOCK(st->st_mode)) {
181b528cefcSMark Murray 	file->mode[0] = 's';
1825e9cd1aeSAssar Westerlund 	file_type = '=';
1835e9cd1aeSAssar Westerlund     }
184b528cefcSMark Murray #ifdef S_ISWHT
1855e9cd1aeSAssar Westerlund     else if(S_ISWHT(st->st_mode)) {
186b528cefcSMark Murray 	file->mode[0] = 'w';
1875e9cd1aeSAssar Westerlund 	file_type = '%';
1885e9cd1aeSAssar Westerlund     }
189b528cefcSMark Murray #endif
190b528cefcSMark Murray     else
191b528cefcSMark Murray 	file->mode[0] = '?';
192b528cefcSMark Murray     {
193b528cefcSMark Murray 	char *x[] = { "---", "--x", "-w-", "-wx",
194b528cefcSMark Murray 		      "r--", "r-x", "rw-", "rwx" };
195b528cefcSMark Murray 	strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
196b528cefcSMark Murray 	strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
197b528cefcSMark Murray 	strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
198b528cefcSMark Murray 	if((st->st_mode & S_ISUID)) {
199b528cefcSMark Murray 	    if((st->st_mode & S_IXUSR))
200b528cefcSMark Murray 		file->mode[3] = 's';
201b528cefcSMark Murray 	    else
202b528cefcSMark Murray 		file->mode[3] = 'S';
203b528cefcSMark Murray 	}
204b528cefcSMark Murray 	if((st->st_mode & S_ISGID)) {
205b528cefcSMark Murray 	    if((st->st_mode & S_IXGRP))
206b528cefcSMark Murray 		file->mode[6] = 's';
207b528cefcSMark Murray 	    else
208b528cefcSMark Murray 		file->mode[6] = 'S';
209b528cefcSMark Murray 	}
210b528cefcSMark Murray 	if((st->st_mode & S_ISTXT)) {
211b528cefcSMark Murray 	    if((st->st_mode & S_IXOTH))
212b528cefcSMark Murray 		file->mode[9] = 't';
213b528cefcSMark Murray 	    else
214b528cefcSMark Murray 		file->mode[9] = 'T';
215b528cefcSMark Murray 	}
216b528cefcSMark Murray     }
217b528cefcSMark Murray     file->n_link = st->st_nlink;
218b528cefcSMark Murray     {
219b528cefcSMark Murray 	struct passwd *pwd;
220b528cefcSMark Murray 	pwd = getpwuid(st->st_uid);
221b528cefcSMark Murray 	if(pwd == NULL)
222b528cefcSMark Murray 	    asprintf(&file->user, "%u", (unsigned)st->st_uid);
223b528cefcSMark Murray 	else
224b528cefcSMark Murray 	    file->user = strdup(pwd->pw_name);
225b528cefcSMark Murray     }
226b528cefcSMark Murray     {
227b528cefcSMark Murray 	struct group *grp;
228b528cefcSMark Murray 	grp = getgrgid(st->st_gid);
229b528cefcSMark Murray 	if(grp == NULL)
230b528cefcSMark Murray 	    asprintf(&file->group, "%u", (unsigned)st->st_gid);
231b528cefcSMark Murray 	else
232b528cefcSMark Murray 	    file->group = strdup(grp->gr_name);
233b528cefcSMark Murray     }
234b528cefcSMark Murray 
235b528cefcSMark Murray     if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
236b528cefcSMark Murray #if defined(major) && defined(minor)
237b528cefcSMark Murray 	asprintf(&file->major, "%u", (unsigned)major(st->st_rdev));
238b528cefcSMark Murray 	asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev));
239b528cefcSMark Murray #else
240b528cefcSMark Murray 	/* Don't want to use the DDI/DKI crap. */
241b528cefcSMark Murray 	asprintf(&file->major, "%u", (unsigned)st->st_rdev);
242b528cefcSMark Murray 	asprintf(&file->minor, "%u", 0);
243b528cefcSMark Murray #endif
244b528cefcSMark Murray     } else
245b528cefcSMark Murray 	asprintf(&file->size, "%lu", (unsigned long)st->st_size);
246b528cefcSMark Murray 
247b528cefcSMark Murray     {
248b528cefcSMark Murray 	time_t t = time(NULL);
2495e9cd1aeSAssar Westerlund 	time_t mtime = st->st_mtime;
2505e9cd1aeSAssar Westerlund 	struct tm *tm = localtime(&mtime);
2515e9cd1aeSAssar Westerlund 	if((t - mtime > 6*30*24*60*60) ||
2525e9cd1aeSAssar Westerlund 	   (mtime - t > 6*30*24*60*60))
253b528cefcSMark Murray 	    strftime(buf, sizeof(buf), "%b %e  %Y", tm);
254b528cefcSMark Murray 	else
255b528cefcSMark Murray 	    strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
256b528cefcSMark Murray 	file->date = strdup(buf);
257b528cefcSMark Murray     }
258b528cefcSMark Murray     {
259b528cefcSMark Murray 	const char *p = strrchr(filename, '/');
260b528cefcSMark Murray 	if(p)
261b528cefcSMark Murray 	    p++;
262b528cefcSMark Murray 	else
263b528cefcSMark Murray 	    p = filename;
2645e9cd1aeSAssar Westerlund 	if((flags & LS_TYPE) && file_type != 0)
2655e9cd1aeSAssar Westerlund 	    asprintf(&file->filename, "%s%c", p, file_type);
2665e9cd1aeSAssar Westerlund 	else
267b528cefcSMark Murray 	    file->filename = strdup(p);
268b528cefcSMark Murray     }
269b528cefcSMark Murray     if(S_ISLNK(st->st_mode)) {
270b528cefcSMark Murray 	int n;
271b528cefcSMark Murray 	n = readlink((char *)filename, buf, sizeof(buf));
272b528cefcSMark Murray 	if(n >= 0) {
273b528cefcSMark Murray 	    buf[n] = '\0';
274b528cefcSMark Murray 	    file->link = strdup(buf);
275b528cefcSMark Murray 	} else
2764137ff4cSJacques Vidrine 	    sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
277b528cefcSMark Murray     }
278b528cefcSMark Murray }
279b528cefcSMark Murray 
280b528cefcSMark Murray static void
281b528cefcSMark Murray print_file(FILE *out,
282b528cefcSMark Murray 	   int flags,
283b528cefcSMark Murray 	   struct fileinfo *f,
284b528cefcSMark Murray 	   int max_inode,
285b528cefcSMark Murray 	   int max_bsize,
286b528cefcSMark Murray 	   int max_n_link,
287b528cefcSMark Murray 	   int max_user,
288b528cefcSMark Murray 	   int max_group,
289b528cefcSMark Murray 	   int max_size,
290b528cefcSMark Murray 	   int max_major,
291b528cefcSMark Murray 	   int max_minor,
292b528cefcSMark Murray 	   int max_date)
293b528cefcSMark Murray {
294b528cefcSMark Murray     if(f->filename == NULL)
295b528cefcSMark Murray 	return;
296b528cefcSMark Murray 
297b528cefcSMark Murray     if(flags & LS_INODE) {
298b528cefcSMark Murray 	sec_fprintf2(out, "%*d", max_inode, f->inode);
299b528cefcSMark Murray 	sec_fprintf2(out, "  ");
300b528cefcSMark Murray     }
301b528cefcSMark Murray     if(flags & LS_SIZE) {
302b528cefcSMark Murray 	sec_fprintf2(out, "%*d", max_bsize, f->bsize);
303b528cefcSMark Murray 	sec_fprintf2(out, "  ");
304b528cefcSMark Murray     }
305b528cefcSMark Murray     sec_fprintf2(out, "%s", f->mode);
306b528cefcSMark Murray     sec_fprintf2(out, "  ");
307b528cefcSMark Murray     sec_fprintf2(out, "%*d", max_n_link, f->n_link);
308b528cefcSMark Murray     sec_fprintf2(out, " ");
309b528cefcSMark Murray     sec_fprintf2(out, "%-*s", max_user, f->user);
310b528cefcSMark Murray     sec_fprintf2(out, "  ");
311b528cefcSMark Murray     sec_fprintf2(out, "%-*s", max_group, f->group);
312b528cefcSMark Murray     sec_fprintf2(out, "  ");
313b528cefcSMark Murray     if(f->major != NULL && f->minor != NULL)
314b528cefcSMark Murray 	sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
315b528cefcSMark Murray     else
316b528cefcSMark Murray 	sec_fprintf2(out, "%*s", max_size, f->size);
317b528cefcSMark Murray     sec_fprintf2(out, " ");
318b528cefcSMark Murray     sec_fprintf2(out, "%*s", max_date, f->date);
319b528cefcSMark Murray     sec_fprintf2(out, " ");
320b528cefcSMark Murray     sec_fprintf2(out, "%s", f->filename);
321b528cefcSMark Murray     if(f->link)
322b528cefcSMark Murray 	sec_fprintf2(out, " -> %s", f->link);
323b528cefcSMark Murray     sec_fprintf2(out, "\r\n");
324b528cefcSMark Murray }
325b528cefcSMark Murray 
326b528cefcSMark Murray static int
327b528cefcSMark Murray compare_filename(struct fileinfo *a, struct fileinfo *b)
328b528cefcSMark Murray {
329b528cefcSMark Murray     if(a->filename == NULL)
330b528cefcSMark Murray 	return 1;
331b528cefcSMark Murray     if(b->filename == NULL)
332b528cefcSMark Murray 	return -1;
333b528cefcSMark Murray     return strcmp(a->filename, b->filename);
334b528cefcSMark Murray }
335b528cefcSMark Murray 
336b528cefcSMark Murray static int
337b528cefcSMark Murray compare_mtime(struct fileinfo *a, struct fileinfo *b)
338b528cefcSMark Murray {
339b528cefcSMark Murray     if(a->filename == NULL)
340b528cefcSMark Murray 	return 1;
341b528cefcSMark Murray     if(b->filename == NULL)
342b528cefcSMark Murray 	return -1;
3435e9cd1aeSAssar Westerlund     return b->st.st_mtime - a->st.st_mtime;
344b528cefcSMark Murray }
345b528cefcSMark Murray 
346b528cefcSMark Murray static int
347b528cefcSMark Murray compare_size(struct fileinfo *a, struct fileinfo *b)
348b528cefcSMark Murray {
349b528cefcSMark Murray     if(a->filename == NULL)
350b528cefcSMark Murray 	return 1;
351b528cefcSMark Murray     if(b->filename == NULL)
352b528cefcSMark Murray 	return -1;
3535e9cd1aeSAssar Westerlund     return b->st.st_size - a->st.st_size;
354b528cefcSMark Murray }
355b528cefcSMark Murray 
3568373020dSJacques Vidrine static int list_dir(FILE*, const char*, int);
357b528cefcSMark Murray 
358b528cefcSMark Murray static int
359b528cefcSMark Murray log10(int num)
360b528cefcSMark Murray {
361b528cefcSMark Murray     int i = 1;
362b528cefcSMark Murray     while(num > 10) {
363b528cefcSMark Murray 	i++;
364b528cefcSMark Murray 	num /= 10;
365b528cefcSMark Murray     }
366b528cefcSMark Murray     return i;
367b528cefcSMark Murray }
368b528cefcSMark Murray 
369b528cefcSMark Murray /*
370b528cefcSMark Murray  * Operate as lstat but fake up entries for AFS mount points so we don't
371b528cefcSMark Murray  * have to fetch them.
372b528cefcSMark Murray  */
373b528cefcSMark Murray 
3745e9cd1aeSAssar Westerlund #ifdef KRB4
3755e9cd1aeSAssar Westerlund static int do_the_afs_dance = 1;
3765e9cd1aeSAssar Westerlund #endif
3775e9cd1aeSAssar Westerlund 
378b528cefcSMark Murray static int
379b528cefcSMark Murray lstat_file (const char *file, struct stat *sb)
380b528cefcSMark Murray {
381b528cefcSMark Murray #ifdef KRB4
3825e9cd1aeSAssar Westerlund     if (do_the_afs_dance &&
3835e9cd1aeSAssar Westerlund 	k_hasafs()
384b528cefcSMark Murray 	&& strcmp(file, ".")
3855e9cd1aeSAssar Westerlund 	&& strcmp(file, "..")
3865e9cd1aeSAssar Westerlund 	&& strcmp(file, "/"))
387b528cefcSMark Murray     {
388b528cefcSMark Murray 	struct ViceIoctl    a_params;
3895e9cd1aeSAssar Westerlund 	char               *dir, *last;
390b528cefcSMark Murray 	char               *path_bkp;
391b528cefcSMark Murray 	static ino_t	   ino_counter = 0, ino_last = 0;
392b528cefcSMark Murray 	int		   ret;
393b528cefcSMark Murray 	const int	   maxsize = 2048;
394b528cefcSMark Murray 
395b528cefcSMark Murray 	path_bkp = strdup (file);
396b528cefcSMark Murray 	if (path_bkp == NULL)
397b528cefcSMark Murray 	    return -1;
398b528cefcSMark Murray 
399b528cefcSMark Murray 	a_params.out = malloc (maxsize);
400b528cefcSMark Murray 	if (a_params.out == NULL) {
401b528cefcSMark Murray 	    free (path_bkp);
402b528cefcSMark Murray 	    return -1;
403b528cefcSMark Murray 	}
404b528cefcSMark Murray 
405b528cefcSMark Murray 	/* If path contains more than the filename alone - split it */
406b528cefcSMark Murray 
407b528cefcSMark Murray 	last = strrchr (path_bkp, '/');
408b528cefcSMark Murray 	if (last != NULL) {
4095e9cd1aeSAssar Westerlund 	    if(last[1] == '\0')
4105e9cd1aeSAssar Westerlund 		/* if path ended in /, replace with `.' */
4115e9cd1aeSAssar Westerlund 		a_params.in = ".";
4125e9cd1aeSAssar Westerlund 	    else
413b528cefcSMark Murray 		a_params.in = last + 1;
4145e9cd1aeSAssar Westerlund 	    while(last > path_bkp && *--last == '/');
4155e9cd1aeSAssar Westerlund 	    if(*last != '/' || last != path_bkp) {
4165e9cd1aeSAssar Westerlund 		*++last = '\0';
4175e9cd1aeSAssar Westerlund 		dir = path_bkp;
418b528cefcSMark Murray 	    } else
4195e9cd1aeSAssar Westerlund 		/* we got to the start, so this must be the root dir */
4205e9cd1aeSAssar Westerlund 		dir = "/";
4215e9cd1aeSAssar Westerlund 	} else {
4225e9cd1aeSAssar Westerlund 	    /* file is relative to cdir */
4235e9cd1aeSAssar Westerlund 	    dir = ".";
4245e9cd1aeSAssar Westerlund 	    a_params.in = path_bkp;
4255e9cd1aeSAssar Westerlund 	}
426b528cefcSMark Murray 
427b528cefcSMark Murray 	a_params.in_size  = strlen (a_params.in) + 1;
428b528cefcSMark Murray 	a_params.out_size = maxsize;
429b528cefcSMark Murray 
4305e9cd1aeSAssar Westerlund 	ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
431b528cefcSMark Murray 	free (a_params.out);
432b528cefcSMark Murray 	if (ret < 0) {
433b528cefcSMark Murray 	    free (path_bkp);
434b528cefcSMark Murray 
435b528cefcSMark Murray 	    if (errno != EINVAL)
436b528cefcSMark Murray 		return ret;
437b528cefcSMark Murray 	    else
438b528cefcSMark Murray 		/* if we get EINVAL this is probably not a mountpoint */
439b528cefcSMark Murray 		return lstat (file, sb);
440b528cefcSMark Murray 	}
441b528cefcSMark Murray 
442b528cefcSMark Murray 	/*
443b528cefcSMark Murray 	 * wow this was a mountpoint, lets cook the struct stat
444b528cefcSMark Murray 	 * use . as a prototype
445b528cefcSMark Murray 	 */
446b528cefcSMark Murray 
4475e9cd1aeSAssar Westerlund 	ret = lstat (dir, sb);
448b528cefcSMark Murray 	free (path_bkp);
449b528cefcSMark Murray 	if (ret < 0)
450b528cefcSMark Murray 	    return ret;
451b528cefcSMark Murray 
452b528cefcSMark Murray 	if (ino_last == sb->st_ino)
453b528cefcSMark Murray 	    ino_counter++;
454b528cefcSMark Murray 	else {
455b528cefcSMark Murray 	    ino_last    = sb->st_ino;
456b528cefcSMark Murray 	    ino_counter = 0;
457b528cefcSMark Murray 	}
458b528cefcSMark Murray 	sb->st_ino += ino_counter;
459b528cefcSMark Murray 	sb->st_nlink = 3;
460b528cefcSMark Murray 
461b528cefcSMark Murray 	return 0;
462b528cefcSMark Murray     }
463b528cefcSMark Murray #endif /* KRB4 */
464b528cefcSMark Murray     return lstat (file, sb);
465b528cefcSMark Murray }
466b528cefcSMark Murray 
4674137ff4cSJacques Vidrine #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
4684137ff4cSJacques Vidrine 				((X)[1] == '.' && (X)[2] == '\0')))
4694137ff4cSJacques Vidrine 
4708373020dSJacques Vidrine static int
471b528cefcSMark Murray list_files(FILE *out, const char **files, int n_files, int flags)
472b528cefcSMark Murray {
473b528cefcSMark Murray     struct fileinfo *fi;
474b528cefcSMark Murray     int i;
4754137ff4cSJacques Vidrine     int *dirs = NULL;
4764137ff4cSJacques Vidrine     size_t total_blocks = 0;
4774137ff4cSJacques Vidrine     int n_print = 0;
4788373020dSJacques Vidrine     int ret = 0;
4798373020dSJacques Vidrine 
4808373020dSJacques Vidrine     if(n_files == 0)
4818373020dSJacques Vidrine 	return 0;
4824137ff4cSJacques Vidrine 
4834137ff4cSJacques Vidrine     if(n_files > 1)
4844137ff4cSJacques Vidrine 	flags |= LS_SHOW_DIRNAME;
485b528cefcSMark Murray 
486b528cefcSMark Murray     fi = calloc(n_files, sizeof(*fi));
487b528cefcSMark Murray     if (fi == NULL) {
4888373020dSJacques Vidrine 	syslog(LOG_ERR, "out of memory");
4898373020dSJacques Vidrine 	return -1;
490b528cefcSMark Murray     }
491b528cefcSMark Murray     for(i = 0; i < n_files; i++) {
492b528cefcSMark Murray 	if(lstat_file(files[i], &fi[i].st) < 0) {
493b528cefcSMark Murray 	    sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
494b528cefcSMark Murray 	    fi[i].filename = NULL;
495b528cefcSMark Murray 	} else {
4964137ff4cSJacques Vidrine 	    int include_in_list = 1;
4974137ff4cSJacques Vidrine 	    total_blocks += block_convert(fi[i].st.st_blocks);
4984137ff4cSJacques Vidrine 	    if(S_ISDIR(fi[i].st.st_mode)) {
4994137ff4cSJacques Vidrine 		if(dirs == NULL)
5004137ff4cSJacques Vidrine 		    dirs = calloc(n_files, sizeof(*dirs));
5014137ff4cSJacques Vidrine 		if(dirs == NULL) {
5028373020dSJacques Vidrine 		    syslog(LOG_ERR, "%s: %m", files[i]);
5038373020dSJacques Vidrine 		    ret = -1;
5044137ff4cSJacques Vidrine 		    goto out;
5054137ff4cSJacques Vidrine 		}
5064137ff4cSJacques Vidrine 		dirs[i] = 1;
5074137ff4cSJacques Vidrine 		if((flags & LS_DIRS) == 0)
5084137ff4cSJacques Vidrine 		    include_in_list = 0;
5094137ff4cSJacques Vidrine 	    }
5104137ff4cSJacques Vidrine 	    if(include_in_list) {
5114137ff4cSJacques Vidrine 		make_fileinfo(out, files[i], &fi[i], flags);
5124137ff4cSJacques Vidrine 		n_print++;
513b528cefcSMark Murray 	    }
514b528cefcSMark Murray 	}
515b528cefcSMark Murray     }
516b528cefcSMark Murray     switch(SORT_MODE(flags)) {
517b528cefcSMark Murray     case LS_SORT_NAME:
518b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
519b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_filename);
520b528cefcSMark Murray 	break;
521b528cefcSMark Murray     case LS_SORT_MTIME:
522b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
523b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_mtime);
524b528cefcSMark Murray 	break;
525b528cefcSMark Murray     case LS_SORT_SIZE:
526b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
527b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_size);
528b528cefcSMark Murray 	break;
529b528cefcSMark Murray     }
5305e9cd1aeSAssar Westerlund     if(DISP_MODE(flags) == LS_DISP_LONG) {
531b528cefcSMark Murray 	int max_inode = 0;
532b528cefcSMark Murray 	int max_bsize = 0;
533b528cefcSMark Murray 	int max_n_link = 0;
534b528cefcSMark Murray 	int max_user = 0;
535b528cefcSMark Murray 	int max_group = 0;
536b528cefcSMark Murray 	int max_size = 0;
537b528cefcSMark Murray 	int max_major = 0;
538b528cefcSMark Murray 	int max_minor = 0;
539b528cefcSMark Murray 	int max_date = 0;
540b528cefcSMark Murray 	for(i = 0; i < n_files; i++) {
541b528cefcSMark Murray 	    if(fi[i].filename == NULL)
542b528cefcSMark Murray 		continue;
543b528cefcSMark Murray 	    if(fi[i].inode > max_inode)
544b528cefcSMark Murray 		max_inode = fi[i].inode;
545b528cefcSMark Murray 	    if(fi[i].bsize > max_bsize)
546b528cefcSMark Murray 		max_bsize = fi[i].bsize;
547b528cefcSMark Murray 	    if(fi[i].n_link > max_n_link)
548b528cefcSMark Murray 		max_n_link = fi[i].n_link;
549b528cefcSMark Murray 	    if(strlen(fi[i].user) > max_user)
550b528cefcSMark Murray 		max_user = strlen(fi[i].user);
551b528cefcSMark Murray 	    if(strlen(fi[i].group) > max_group)
552b528cefcSMark Murray 		max_group = strlen(fi[i].group);
553b528cefcSMark Murray 	    if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
554b528cefcSMark Murray 		max_major = strlen(fi[i].major);
555b528cefcSMark Murray 	    if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
556b528cefcSMark Murray 		max_minor = strlen(fi[i].minor);
557b528cefcSMark Murray 	    if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
558b528cefcSMark Murray 		max_size = strlen(fi[i].size);
559b528cefcSMark Murray 	    if(strlen(fi[i].date) > max_date)
560b528cefcSMark Murray 		max_date = strlen(fi[i].date);
561b528cefcSMark Murray 	}
562b528cefcSMark Murray 	if(max_size < max_major + max_minor + 2)
563b528cefcSMark Murray 	    max_size = max_major + max_minor + 2;
564b528cefcSMark Murray 	else if(max_size - max_minor - 2 > max_major)
565b528cefcSMark Murray 	    max_major = max_size - max_minor - 2;
566b528cefcSMark Murray 	max_inode = log10(max_inode);
567b528cefcSMark Murray 	max_bsize = log10(max_bsize);
568b528cefcSMark Murray 	max_n_link = log10(max_n_link);
569b528cefcSMark Murray 
5704137ff4cSJacques Vidrine 	if(n_print > 0)
5714137ff4cSJacques Vidrine 	    sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
572b528cefcSMark Murray 	if(flags & LS_SORT_REVERSE)
573b528cefcSMark Murray 	    for(i = n_files - 1; i >= 0; i--)
574b528cefcSMark Murray 		print_file(out,
575b528cefcSMark Murray 			   flags,
576b528cefcSMark Murray 			   &fi[i],
577b528cefcSMark Murray 			   max_inode,
578b528cefcSMark Murray 			   max_bsize,
579b528cefcSMark Murray 			   max_n_link,
580b528cefcSMark Murray 			   max_user,
581b528cefcSMark Murray 			   max_group,
582b528cefcSMark Murray 			   max_size,
583b528cefcSMark Murray 			   max_major,
584b528cefcSMark Murray 			   max_minor,
585b528cefcSMark Murray 			   max_date);
586b528cefcSMark Murray 	else
587b528cefcSMark Murray 	    for(i = 0; i < n_files; i++)
588b528cefcSMark Murray 		print_file(out,
589b528cefcSMark Murray 			   flags,
590b528cefcSMark Murray 			   &fi[i],
591b528cefcSMark Murray 			   max_inode,
592b528cefcSMark Murray 			   max_bsize,
593b528cefcSMark Murray 			   max_n_link,
594b528cefcSMark Murray 			   max_user,
595b528cefcSMark Murray 			   max_group,
596b528cefcSMark Murray 			   max_size,
597b528cefcSMark Murray 			   max_major,
598b528cefcSMark Murray 			   max_minor,
599b528cefcSMark Murray 			   max_date);
6005e9cd1aeSAssar Westerlund     } else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
6015e9cd1aeSAssar Westerlund 	      DISP_MODE(flags) == LS_DISP_CROSS) {
6025e9cd1aeSAssar Westerlund 	int max_len = 0;
6034137ff4cSJacques Vidrine 	int size_len = 0;
6045e9cd1aeSAssar Westerlund 	int num_files = n_files;
6055e9cd1aeSAssar Westerlund 	int columns;
6065e9cd1aeSAssar Westerlund 	int j;
6075e9cd1aeSAssar Westerlund 	for(i = 0; i < n_files; i++) {
6085e9cd1aeSAssar Westerlund 	    if(fi[i].filename == NULL) {
6095e9cd1aeSAssar Westerlund 		num_files--;
6105e9cd1aeSAssar Westerlund 		continue;
6115e9cd1aeSAssar Westerlund 	    }
6125e9cd1aeSAssar Westerlund 	    if(strlen(fi[i].filename) > max_len)
6135e9cd1aeSAssar Westerlund 		max_len = strlen(fi[i].filename);
6144137ff4cSJacques Vidrine 	    if(log10(fi[i].bsize) > size_len)
6154137ff4cSJacques Vidrine 		size_len = log10(fi[i].bsize);
6165e9cd1aeSAssar Westerlund 	}
6174137ff4cSJacques Vidrine 	if(num_files == 0)
6184137ff4cSJacques Vidrine 	    goto next;
6194137ff4cSJacques Vidrine 	if(flags & LS_SIZE) {
6204137ff4cSJacques Vidrine 	    columns = 80 / (size_len + 1 + max_len + 1);
6214137ff4cSJacques Vidrine 	    max_len = 80 / columns - size_len - 1;
6224137ff4cSJacques Vidrine 	} else {
6235e9cd1aeSAssar Westerlund 	    columns = 80 / (max_len + 1); /* get space between columns */
6245e9cd1aeSAssar Westerlund 	    max_len = 80 / columns;
6254137ff4cSJacques Vidrine 	}
6264137ff4cSJacques Vidrine 	if(flags & LS_SIZE)
6274137ff4cSJacques Vidrine 	    sec_fprintf2(out, "total %lu\r\n",
6284137ff4cSJacques Vidrine 			 (unsigned long)total_blocks);
6295e9cd1aeSAssar Westerlund 	if(DISP_MODE(flags) == LS_DISP_CROSS) {
6305e9cd1aeSAssar Westerlund 	    for(i = 0, j = 0; i < n_files; i++) {
6315e9cd1aeSAssar Westerlund 		if(fi[i].filename == NULL)
6325e9cd1aeSAssar Westerlund 		    continue;
6334137ff4cSJacques Vidrine 		if(flags & LS_SIZE)
6344137ff4cSJacques Vidrine 		    sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
6354137ff4cSJacques Vidrine 				 max_len, fi[i].filename);
6364137ff4cSJacques Vidrine 		else
6375e9cd1aeSAssar Westerlund 		    sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
6385e9cd1aeSAssar Westerlund 		j++;
6395e9cd1aeSAssar Westerlund 		if(j == columns) {
6405e9cd1aeSAssar Westerlund 		    sec_fprintf2(out, "\r\n");
6415e9cd1aeSAssar Westerlund 		    j = 0;
6425e9cd1aeSAssar Westerlund 		}
6435e9cd1aeSAssar Westerlund 	    }
6445e9cd1aeSAssar Westerlund 	    if(j > 0)
6455e9cd1aeSAssar Westerlund 		sec_fprintf2(out, "\r\n");
6465e9cd1aeSAssar Westerlund 	} else {
6475e9cd1aeSAssar Westerlund 	    int skip = (num_files + columns - 1) / columns;
6485e9cd1aeSAssar Westerlund 	    j = 0;
6495e9cd1aeSAssar Westerlund 	    for(i = 0; i < skip; i++) {
6505e9cd1aeSAssar Westerlund 		for(j = i; j < n_files;) {
6515e9cd1aeSAssar Westerlund 		    while(j < n_files && fi[j].filename == NULL)
6525e9cd1aeSAssar Westerlund 			j++;
6534137ff4cSJacques Vidrine 		    if(flags & LS_SIZE)
6544137ff4cSJacques Vidrine 			sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
6554137ff4cSJacques Vidrine 				     max_len, fi[j].filename);
6564137ff4cSJacques Vidrine 		    else
6575e9cd1aeSAssar Westerlund 			sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
6585e9cd1aeSAssar Westerlund 		    j += skip;
6595e9cd1aeSAssar Westerlund 		}
6605e9cd1aeSAssar Westerlund 		sec_fprintf2(out, "\r\n");
6615e9cd1aeSAssar Westerlund 	    }
6625e9cd1aeSAssar Westerlund 	}
6635e9cd1aeSAssar Westerlund     } else {
6645e9cd1aeSAssar Westerlund 	for(i = 0; i < n_files; i++) {
6655e9cd1aeSAssar Westerlund 	    if(fi[i].filename == NULL)
6665e9cd1aeSAssar Westerlund 		continue;
6675e9cd1aeSAssar Westerlund 	    sec_fprintf2(out, "%s\r\n", fi[i].filename);
6685e9cd1aeSAssar Westerlund 	}
6695e9cd1aeSAssar Westerlund     }
6704137ff4cSJacques Vidrine  next:
6714137ff4cSJacques Vidrine     if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
6724137ff4cSJacques Vidrine 	for(i = 0; i < n_files; i++) {
6734137ff4cSJacques Vidrine 	    if(dirs[i]) {
6744137ff4cSJacques Vidrine 		const char *p = strrchr(files[i], '/');
6754137ff4cSJacques Vidrine 		if(p == NULL)
6764137ff4cSJacques Vidrine 		    p = files[i];
6774137ff4cSJacques Vidrine 		else
6784137ff4cSJacques Vidrine 		    p++;
6794137ff4cSJacques Vidrine 		if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
6804137ff4cSJacques Vidrine 		    if((flags & LS_SHOW_DIRNAME)) {
6814137ff4cSJacques Vidrine 			if ((flags & LS_EXTRA_BLANK))
6824137ff4cSJacques Vidrine 			    sec_fprintf2(out, "\r\n");
6834137ff4cSJacques Vidrine 			sec_fprintf2(out, "%s:\r\n", files[i]);
6844137ff4cSJacques Vidrine 		    }
6854137ff4cSJacques Vidrine 		    list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
6864137ff4cSJacques Vidrine 		}
6874137ff4cSJacques Vidrine 	    }
6884137ff4cSJacques Vidrine 	}
6894137ff4cSJacques Vidrine     }
6904137ff4cSJacques Vidrine  out:
691b528cefcSMark Murray     for(i = 0; i < n_files; i++)
692b528cefcSMark Murray 	free_fileinfo(&fi[i]);
693b528cefcSMark Murray     free(fi);
6944137ff4cSJacques Vidrine     if(dirs != NULL)
6954137ff4cSJacques Vidrine 	free(dirs);
6968373020dSJacques Vidrine     return ret;
697b528cefcSMark Murray }
698b528cefcSMark Murray 
699b528cefcSMark Murray static void
700b528cefcSMark Murray free_files (char **files, int n)
701b528cefcSMark Murray {
702b528cefcSMark Murray     int i;
703b528cefcSMark Murray 
704b528cefcSMark Murray     for (i = 0; i < n; ++i)
705b528cefcSMark Murray 	free (files[i]);
706b528cefcSMark Murray     free (files);
707b528cefcSMark Murray }
708b528cefcSMark Murray 
7094137ff4cSJacques Vidrine static int
7104137ff4cSJacques Vidrine hide_file(const char *filename, int flags)
7114137ff4cSJacques Vidrine {
7124137ff4cSJacques Vidrine     if(filename[0] != '.')
7134137ff4cSJacques Vidrine 	return 0;
7144137ff4cSJacques Vidrine     if((flags & LS_IGNORE_DOT))
7154137ff4cSJacques Vidrine 	return 1;
7164137ff4cSJacques Vidrine     if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
7174137ff4cSJacques Vidrine 	if((flags & LS_SHOW_ALL))
7184137ff4cSJacques Vidrine 	    return 0;
7194137ff4cSJacques Vidrine 	else
7204137ff4cSJacques Vidrine 	    return 1;
7214137ff4cSJacques Vidrine     }
7224137ff4cSJacques Vidrine     return 0;
7234137ff4cSJacques Vidrine }
7244137ff4cSJacques Vidrine 
7258373020dSJacques Vidrine static int
726b528cefcSMark Murray list_dir(FILE *out, const char *directory, int flags)
727b528cefcSMark Murray {
728b528cefcSMark Murray     DIR *d = opendir(directory);
729b528cefcSMark Murray     struct dirent *ent;
730b528cefcSMark Murray     char **files = NULL;
731b528cefcSMark Murray     int n_files = 0;
732b528cefcSMark Murray 
733b528cefcSMark Murray     if(d == NULL) {
7348373020dSJacques Vidrine 	syslog(LOG_ERR, "%s: %m", directory);
7358373020dSJacques Vidrine 	return -1;
736b528cefcSMark Murray     }
737b528cefcSMark Murray     while((ent = readdir(d)) != NULL) {
738b528cefcSMark Murray 	void *tmp;
739b528cefcSMark Murray 
7404137ff4cSJacques Vidrine 	if(hide_file(ent->d_name, flags))
741b528cefcSMark Murray 	    continue;
742b528cefcSMark Murray 	tmp = realloc(files, (n_files + 1) * sizeof(*files));
743b528cefcSMark Murray 	if (tmp == NULL) {
7448373020dSJacques Vidrine 	    syslog(LOG_ERR, "%s: out of memory", directory);
745b528cefcSMark Murray 	    free_files (files, n_files);
746b528cefcSMark Murray 	    closedir (d);
7478373020dSJacques Vidrine 	    return -1;
748b528cefcSMark Murray 	}
749b528cefcSMark Murray 	files = tmp;
750b528cefcSMark Murray 	asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
751b528cefcSMark Murray 	if (files[n_files] == NULL) {
7528373020dSJacques Vidrine 	    syslog(LOG_ERR, "%s: out of memory", directory);
753b528cefcSMark Murray 	    free_files (files, n_files);
754b528cefcSMark Murray 	    closedir (d);
7558373020dSJacques Vidrine 	    return -1;
756b528cefcSMark Murray 	}
757b528cefcSMark Murray 	++n_files;
758b528cefcSMark Murray     }
759b528cefcSMark Murray     closedir(d);
7608373020dSJacques Vidrine     return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
761b528cefcSMark Murray }
762b528cefcSMark Murray 
7634137ff4cSJacques Vidrine static int
7644137ff4cSJacques Vidrine parse_flags(const char *options)
765b528cefcSMark Murray {
7664137ff4cSJacques Vidrine #ifdef TEST
7674137ff4cSJacques Vidrine     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
7684137ff4cSJacques Vidrine #else
7695e9cd1aeSAssar Westerlund     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
7704137ff4cSJacques Vidrine #endif
771b528cefcSMark Murray 
772b528cefcSMark Murray     const char *p;
7734137ff4cSJacques Vidrine     if(options == NULL || *options != '-')
7744137ff4cSJacques Vidrine 	return flags;
7754137ff4cSJacques Vidrine     for(p = options + 1; *p; p++) {
776b528cefcSMark Murray 	switch(*p) {
7775e9cd1aeSAssar Westerlund 	case '1':
7785e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE);
7795e9cd1aeSAssar Westerlund 	    break;
780b528cefcSMark Murray 	case 'a':
7814137ff4cSJacques Vidrine 	    flags |= LS_SHOW_ALL;
7824137ff4cSJacques Vidrine 	    /*FALLTHROUGH*/
783b528cefcSMark Murray 	case 'A':
784b528cefcSMark Murray 	    flags &= ~LS_IGNORE_DOT;
785b528cefcSMark Murray 	    break;
786b528cefcSMark Murray 	case 'C':
7875e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
788b528cefcSMark Murray 	    break;
789b528cefcSMark Murray 	case 'd':
790b528cefcSMark Murray 	    flags |= LS_DIRS;
791b528cefcSMark Murray 	    break;
792b528cefcSMark Murray 	case 'f':
793b528cefcSMark Murray 	    flags = (flags & ~LS_SORT_MODE);
794b528cefcSMark Murray 	    break;
7955e9cd1aeSAssar Westerlund 	case 'F':
7965e9cd1aeSAssar Westerlund 	    flags |= LS_TYPE;
7975e9cd1aeSAssar Westerlund 	    break;
798b528cefcSMark Murray 	case 'i':
7995e9cd1aeSAssar Westerlund 	    flags |= LS_INODE;
800b528cefcSMark Murray 	    break;
801b528cefcSMark Murray 	case 'l':
8025e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
803b528cefcSMark Murray 	    break;
8044137ff4cSJacques Vidrine 	case 'r':
8054137ff4cSJacques Vidrine 	    flags |= LS_SORT_REVERSE;
8064137ff4cSJacques Vidrine 	    break;
8074137ff4cSJacques Vidrine 	case 'R':
8084137ff4cSJacques Vidrine 	    flags |= LS_RECURSIVE;
809b528cefcSMark Murray 	    break;
810b528cefcSMark Murray 	case 's':
811b528cefcSMark Murray 	    flags |= LS_SIZE;
812b528cefcSMark Murray 	    break;
813b528cefcSMark Murray 	case 'S':
814b528cefcSMark Murray 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
815b528cefcSMark Murray 	    break;
8164137ff4cSJacques Vidrine 	case 't':
8174137ff4cSJacques Vidrine 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
818b528cefcSMark Murray 	    break;
8195e9cd1aeSAssar Westerlund 	case 'x':
8205e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
8215e9cd1aeSAssar Westerlund 	    break;
8224137ff4cSJacques Vidrine 	    /* these are a bunch of unimplemented flags from BSD ls */
8234137ff4cSJacques Vidrine 	case 'k': /* display sizes in kB */
8244137ff4cSJacques Vidrine 	case 'c': /* last change time */
8254137ff4cSJacques Vidrine 	case 'L': /* list symlink target */
8264137ff4cSJacques Vidrine 	case 'm': /* stream output */
8274137ff4cSJacques Vidrine 	case 'o': /* BSD file flags */
8284137ff4cSJacques Vidrine 	case 'p': /* display / after directories */
8294137ff4cSJacques Vidrine 	case 'q': /* print non-graphic characters */
8304137ff4cSJacques Vidrine 	case 'u': /* use last access time */
8314137ff4cSJacques Vidrine 	case 'T': /* display complete time */
8324137ff4cSJacques Vidrine 	case 'W': /* include whiteouts */
8334137ff4cSJacques Vidrine 	    break;
834b528cefcSMark Murray 	}
835b528cefcSMark Murray     }
8364137ff4cSJacques Vidrine     return flags;
8374137ff4cSJacques Vidrine }
8384137ff4cSJacques Vidrine 
8398373020dSJacques Vidrine int
8404137ff4cSJacques Vidrine builtin_ls(FILE *out, const char *file)
8414137ff4cSJacques Vidrine {
8424137ff4cSJacques Vidrine     int flags;
8438373020dSJacques Vidrine     int ret;
8444137ff4cSJacques Vidrine 
8454137ff4cSJacques Vidrine     if(*file == '-') {
8464137ff4cSJacques Vidrine 	flags = parse_flags(file);
847b528cefcSMark Murray 	file = ".";
8484137ff4cSJacques Vidrine     } else
8494137ff4cSJacques Vidrine 	flags = parse_flags("");
8504137ff4cSJacques Vidrine 
8518373020dSJacques Vidrine     ret = list_files(out, &file, 1, flags);
852b528cefcSMark Murray     sec_fflush(out);
8538373020dSJacques Vidrine     return ret;
854b528cefcSMark Murray }
855