xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ls.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2*ae771770SStanislav Sedov  * 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 
36*ae771770SStanislav Sedov RCSID("$Id$");
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
main(int argc,char ** argv)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
free_fileinfo(struct fileinfo * f)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
block_convert(size_t blocks)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 
149c19800e8SDoug Rabson static int
make_fileinfo(FILE * out,const char * filename,struct fileinfo * file,int flags)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);
221c19800e8SDoug Rabson 	if(pwd == NULL) {
222c19800e8SDoug Rabson 	    if (asprintf(&file->user, "%u", (unsigned)st->st_uid) == -1)
223c19800e8SDoug Rabson 		file->user = NULL;
224c19800e8SDoug Rabson 	} else
225b528cefcSMark Murray 	    file->user = strdup(pwd->pw_name);
226c19800e8SDoug Rabson 	if (file->user == NULL) {
227c19800e8SDoug Rabson 	    syslog(LOG_ERR, "out of memory");
228c19800e8SDoug Rabson 	    return -1;
229c19800e8SDoug Rabson 	}
230b528cefcSMark Murray     }
231b528cefcSMark Murray     {
232b528cefcSMark Murray 	struct group *grp;
233b528cefcSMark Murray 	grp = getgrgid(st->st_gid);
234c19800e8SDoug Rabson 	if(grp == NULL) {
235c19800e8SDoug Rabson 	    if (asprintf(&file->group, "%u", (unsigned)st->st_gid) == -1)
236c19800e8SDoug Rabson 		file->group = NULL;
237c19800e8SDoug Rabson 	} else
238b528cefcSMark Murray 	    file->group = strdup(grp->gr_name);
239c19800e8SDoug Rabson 	if (file->group == NULL) {
240c19800e8SDoug Rabson 	    syslog(LOG_ERR, "out of memory");
241c19800e8SDoug Rabson 	    return -1;
242c19800e8SDoug Rabson 	}
243b528cefcSMark Murray     }
244b528cefcSMark Murray 
245b528cefcSMark Murray     if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
246b528cefcSMark Murray #if defined(major) && defined(minor)
247c19800e8SDoug Rabson 	if (asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)) == -1)
248c19800e8SDoug Rabson 	    file->major = NULL;
249c19800e8SDoug Rabson 	if (asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)) == -1)
250c19800e8SDoug Rabson 	    file->minor = NULL;
251b528cefcSMark Murray #else
252b528cefcSMark Murray 	/* Don't want to use the DDI/DKI crap. */
253c19800e8SDoug Rabson 	if (asprintf(&file->major, "%u", (unsigned)st->st_rdev) == -1)
254c19800e8SDoug Rabson 	    file->major = NULL;
255c19800e8SDoug Rabson 	if (asprintf(&file->minor, "%u", 0) == -1)
256c19800e8SDoug Rabson 	    file->minor = NULL;
257b528cefcSMark Murray #endif
258c19800e8SDoug Rabson 	if (file->major == NULL || file->minor == NULL) {
259c19800e8SDoug Rabson 	    syslog(LOG_ERR, "out of memory");
260c19800e8SDoug Rabson 	    return -1;
261c19800e8SDoug Rabson 	}
262c19800e8SDoug Rabson     } else {
263c19800e8SDoug Rabson 	if (asprintf(&file->size, "%lu", (unsigned long)st->st_size) == -1)
264c19800e8SDoug Rabson 	    file->size = NULL;
265c19800e8SDoug Rabson     }
266b528cefcSMark Murray 
267b528cefcSMark Murray     {
268b528cefcSMark Murray 	time_t t = time(NULL);
2695e9cd1aeSAssar Westerlund 	time_t mtime = st->st_mtime;
2705e9cd1aeSAssar Westerlund 	struct tm *tm = localtime(&mtime);
2715e9cd1aeSAssar Westerlund 	if((t - mtime > 6*30*24*60*60) ||
2725e9cd1aeSAssar Westerlund 	   (mtime - t > 6*30*24*60*60))
273b528cefcSMark Murray 	    strftime(buf, sizeof(buf), "%b %e  %Y", tm);
274b528cefcSMark Murray 	else
275b528cefcSMark Murray 	    strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
276b528cefcSMark Murray 	file->date = strdup(buf);
277c19800e8SDoug Rabson 	if (file->date == NULL) {
278c19800e8SDoug Rabson 	    syslog(LOG_ERR, "out of memory");
279c19800e8SDoug Rabson 	    return -1;
280c19800e8SDoug Rabson 	}
281b528cefcSMark Murray     }
282b528cefcSMark Murray     {
283b528cefcSMark Murray 	const char *p = strrchr(filename, '/');
284b528cefcSMark Murray 	if(p)
285b528cefcSMark Murray 	    p++;
286b528cefcSMark Murray 	else
287b528cefcSMark Murray 	    p = filename;
288c19800e8SDoug Rabson 	if((flags & LS_TYPE) && file_type != 0) {
289c19800e8SDoug Rabson 	    if (asprintf(&file->filename, "%s%c", p, file_type) == -1)
290c19800e8SDoug Rabson 		file->filename = NULL;
291c19800e8SDoug Rabson 	} else
292b528cefcSMark Murray 	    file->filename = strdup(p);
293c19800e8SDoug Rabson 	if (file->filename == NULL) {
294c19800e8SDoug Rabson 	    syslog(LOG_ERR, "out of memory");
295c19800e8SDoug Rabson 	    return -1;
296c19800e8SDoug Rabson 	}
297b528cefcSMark Murray     }
298b528cefcSMark Murray     if(S_ISLNK(st->st_mode)) {
299b528cefcSMark Murray 	int n;
300bbd80c28SJacques Vidrine 	n = readlink((char *)filename, buf, sizeof(buf) - 1);
301b528cefcSMark Murray 	if(n >= 0) {
302b528cefcSMark Murray 	    buf[n] = '\0';
303b528cefcSMark Murray 	    file->link = strdup(buf);
304c19800e8SDoug Rabson 	    if (file->link == NULL) {
305c19800e8SDoug Rabson 		syslog(LOG_ERR, "out of memory");
306c19800e8SDoug Rabson 		return -1;
307c19800e8SDoug Rabson 	    }
308b528cefcSMark Murray 	} else
3094137ff4cSJacques Vidrine 	    sec_fprintf2(out, "readlink(%s): %s", filename, strerror(errno));
310b528cefcSMark Murray     }
311c19800e8SDoug Rabson     return 0;
312b528cefcSMark Murray }
313b528cefcSMark Murray 
314b528cefcSMark Murray 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)315b528cefcSMark Murray print_file(FILE *out,
316b528cefcSMark Murray 	   int flags,
317b528cefcSMark Murray 	   struct fileinfo *f,
318b528cefcSMark Murray 	   int max_inode,
319b528cefcSMark Murray 	   int max_bsize,
320b528cefcSMark Murray 	   int max_n_link,
321b528cefcSMark Murray 	   int max_user,
322b528cefcSMark Murray 	   int max_group,
323b528cefcSMark Murray 	   int max_size,
324b528cefcSMark Murray 	   int max_major,
325b528cefcSMark Murray 	   int max_minor,
326b528cefcSMark Murray 	   int max_date)
327b528cefcSMark Murray {
328b528cefcSMark Murray     if(f->filename == NULL)
329b528cefcSMark Murray 	return;
330b528cefcSMark Murray 
331b528cefcSMark Murray     if(flags & LS_INODE) {
332b528cefcSMark Murray 	sec_fprintf2(out, "%*d", max_inode, f->inode);
333b528cefcSMark Murray 	sec_fprintf2(out, "  ");
334b528cefcSMark Murray     }
335b528cefcSMark Murray     if(flags & LS_SIZE) {
336b528cefcSMark Murray 	sec_fprintf2(out, "%*d", max_bsize, f->bsize);
337b528cefcSMark Murray 	sec_fprintf2(out, "  ");
338b528cefcSMark Murray     }
339b528cefcSMark Murray     sec_fprintf2(out, "%s", f->mode);
340b528cefcSMark Murray     sec_fprintf2(out, "  ");
341b528cefcSMark Murray     sec_fprintf2(out, "%*d", max_n_link, f->n_link);
342b528cefcSMark Murray     sec_fprintf2(out, " ");
343b528cefcSMark Murray     sec_fprintf2(out, "%-*s", max_user, f->user);
344b528cefcSMark Murray     sec_fprintf2(out, "  ");
345b528cefcSMark Murray     sec_fprintf2(out, "%-*s", max_group, f->group);
346b528cefcSMark Murray     sec_fprintf2(out, "  ");
347b528cefcSMark Murray     if(f->major != NULL && f->minor != NULL)
348b528cefcSMark Murray 	sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
349b528cefcSMark Murray     else
350b528cefcSMark Murray 	sec_fprintf2(out, "%*s", max_size, f->size);
351b528cefcSMark Murray     sec_fprintf2(out, " ");
352b528cefcSMark Murray     sec_fprintf2(out, "%*s", max_date, f->date);
353b528cefcSMark Murray     sec_fprintf2(out, " ");
354b528cefcSMark Murray     sec_fprintf2(out, "%s", f->filename);
355b528cefcSMark Murray     if(f->link)
356b528cefcSMark Murray 	sec_fprintf2(out, " -> %s", f->link);
357b528cefcSMark Murray     sec_fprintf2(out, "\r\n");
358b528cefcSMark Murray }
359b528cefcSMark Murray 
360b528cefcSMark Murray static int
compare_filename(struct fileinfo * a,struct fileinfo * b)361b528cefcSMark Murray compare_filename(struct fileinfo *a, struct fileinfo *b)
362b528cefcSMark Murray {
363b528cefcSMark Murray     if(a->filename == NULL)
364b528cefcSMark Murray 	return 1;
365b528cefcSMark Murray     if(b->filename == NULL)
366b528cefcSMark Murray 	return -1;
367b528cefcSMark Murray     return strcmp(a->filename, b->filename);
368b528cefcSMark Murray }
369b528cefcSMark Murray 
370b528cefcSMark Murray static int
compare_mtime(struct fileinfo * a,struct fileinfo * b)371b528cefcSMark Murray compare_mtime(struct fileinfo *a, struct fileinfo *b)
372b528cefcSMark Murray {
373b528cefcSMark Murray     if(a->filename == NULL)
374b528cefcSMark Murray 	return 1;
375b528cefcSMark Murray     if(b->filename == NULL)
376b528cefcSMark Murray 	return -1;
3775e9cd1aeSAssar Westerlund     return b->st.st_mtime - a->st.st_mtime;
378b528cefcSMark Murray }
379b528cefcSMark Murray 
380b528cefcSMark Murray static int
compare_size(struct fileinfo * a,struct fileinfo * b)381b528cefcSMark Murray compare_size(struct fileinfo *a, struct fileinfo *b)
382b528cefcSMark Murray {
383b528cefcSMark Murray     if(a->filename == NULL)
384b528cefcSMark Murray 	return 1;
385b528cefcSMark Murray     if(b->filename == NULL)
386b528cefcSMark Murray 	return -1;
3875e9cd1aeSAssar Westerlund     return b->st.st_size - a->st.st_size;
388b528cefcSMark Murray }
389b528cefcSMark Murray 
3908373020dSJacques Vidrine static int list_dir(FILE*, const char*, int);
391b528cefcSMark Murray 
392b528cefcSMark Murray static int
find_log10(int num)393c19800e8SDoug Rabson find_log10(int num)
394b528cefcSMark Murray {
395b528cefcSMark Murray     int i = 1;
396b528cefcSMark Murray     while(num > 10) {
397b528cefcSMark Murray 	i++;
398b528cefcSMark Murray 	num /= 10;
399b528cefcSMark Murray     }
400b528cefcSMark Murray     return i;
401b528cefcSMark Murray }
402b528cefcSMark Murray 
403b528cefcSMark Murray /*
404b528cefcSMark Murray  * Operate as lstat but fake up entries for AFS mount points so we don't
405b528cefcSMark Murray  * have to fetch them.
406b528cefcSMark Murray  */
407b528cefcSMark Murray 
408*ae771770SStanislav Sedov #ifdef KRB5
4095e9cd1aeSAssar Westerlund static int do_the_afs_dance = 1;
4105e9cd1aeSAssar Westerlund #endif
4115e9cd1aeSAssar Westerlund 
412b528cefcSMark Murray static int
lstat_file(const char * file,struct stat * sb)413b528cefcSMark Murray lstat_file (const char *file, struct stat *sb)
414b528cefcSMark Murray {
415*ae771770SStanislav Sedov #ifdef KRB5
4165e9cd1aeSAssar Westerlund     if (do_the_afs_dance &&
4175e9cd1aeSAssar Westerlund 	k_hasafs()
418b528cefcSMark Murray 	&& strcmp(file, ".")
4195e9cd1aeSAssar Westerlund 	&& strcmp(file, "..")
4205e9cd1aeSAssar Westerlund 	&& strcmp(file, "/"))
421b528cefcSMark Murray     {
422b528cefcSMark Murray 	struct ViceIoctl    a_params;
4235e9cd1aeSAssar Westerlund 	char               *dir, *last;
424b528cefcSMark Murray 	char               *path_bkp;
425b528cefcSMark Murray 	static ino_t	   ino_counter = 0, ino_last = 0;
426b528cefcSMark Murray 	int		   ret;
427b528cefcSMark Murray 	const int	   maxsize = 2048;
428b528cefcSMark Murray 
429b528cefcSMark Murray 	path_bkp = strdup (file);
430b528cefcSMark Murray 	if (path_bkp == NULL)
431b528cefcSMark Murray 	    return -1;
432b528cefcSMark Murray 
433b528cefcSMark Murray 	a_params.out = malloc (maxsize);
434b528cefcSMark Murray 	if (a_params.out == NULL) {
435b528cefcSMark Murray 	    free (path_bkp);
436b528cefcSMark Murray 	    return -1;
437b528cefcSMark Murray 	}
438b528cefcSMark Murray 
439b528cefcSMark Murray 	/* If path contains more than the filename alone - split it */
440b528cefcSMark Murray 
441b528cefcSMark Murray 	last = strrchr (path_bkp, '/');
442b528cefcSMark Murray 	if (last != NULL) {
4435e9cd1aeSAssar Westerlund 	    if(last[1] == '\0')
4445e9cd1aeSAssar Westerlund 		/* if path ended in /, replace with `.' */
4455e9cd1aeSAssar Westerlund 		a_params.in = ".";
4465e9cd1aeSAssar Westerlund 	    else
447b528cefcSMark Murray 		a_params.in = last + 1;
4485e9cd1aeSAssar Westerlund 	    while(last > path_bkp && *--last == '/');
4495e9cd1aeSAssar Westerlund 	    if(*last != '/' || last != path_bkp) {
4505e9cd1aeSAssar Westerlund 		*++last = '\0';
4515e9cd1aeSAssar Westerlund 		dir = path_bkp;
452b528cefcSMark Murray 	    } else
4535e9cd1aeSAssar Westerlund 		/* we got to the start, so this must be the root dir */
4545e9cd1aeSAssar Westerlund 		dir = "/";
4555e9cd1aeSAssar Westerlund 	} else {
4565e9cd1aeSAssar Westerlund 	    /* file is relative to cdir */
4575e9cd1aeSAssar Westerlund 	    dir = ".";
4585e9cd1aeSAssar Westerlund 	    a_params.in = path_bkp;
4595e9cd1aeSAssar Westerlund 	}
460b528cefcSMark Murray 
461b528cefcSMark Murray 	a_params.in_size  = strlen (a_params.in) + 1;
462b528cefcSMark Murray 	a_params.out_size = maxsize;
463b528cefcSMark Murray 
4645e9cd1aeSAssar Westerlund 	ret = k_pioctl (dir, VIOC_AFS_STAT_MT_PT, &a_params, 0);
465b528cefcSMark Murray 	free (a_params.out);
466b528cefcSMark Murray 	if (ret < 0) {
467b528cefcSMark Murray 	    free (path_bkp);
468b528cefcSMark Murray 
469b528cefcSMark Murray 	    if (errno != EINVAL)
470b528cefcSMark Murray 		return ret;
471b528cefcSMark Murray 	    else
472b528cefcSMark Murray 		/* if we get EINVAL this is probably not a mountpoint */
473b528cefcSMark Murray 		return lstat (file, sb);
474b528cefcSMark Murray 	}
475b528cefcSMark Murray 
476b528cefcSMark Murray 	/*
477b528cefcSMark Murray 	 * wow this was a mountpoint, lets cook the struct stat
478b528cefcSMark Murray 	 * use . as a prototype
479b528cefcSMark Murray 	 */
480b528cefcSMark Murray 
4815e9cd1aeSAssar Westerlund 	ret = lstat (dir, sb);
482b528cefcSMark Murray 	free (path_bkp);
483b528cefcSMark Murray 	if (ret < 0)
484b528cefcSMark Murray 	    return ret;
485b528cefcSMark Murray 
486b528cefcSMark Murray 	if (ino_last == sb->st_ino)
487b528cefcSMark Murray 	    ino_counter++;
488b528cefcSMark Murray 	else {
489b528cefcSMark Murray 	    ino_last    = sb->st_ino;
490b528cefcSMark Murray 	    ino_counter = 0;
491b528cefcSMark Murray 	}
492b528cefcSMark Murray 	sb->st_ino += ino_counter;
493b528cefcSMark Murray 	sb->st_nlink = 3;
494b528cefcSMark Murray 
495b528cefcSMark Murray 	return 0;
496b528cefcSMark Murray     }
497*ae771770SStanislav Sedov #endif /* KRB5 */
498b528cefcSMark Murray     return lstat (file, sb);
499b528cefcSMark Murray }
500b528cefcSMark Murray 
5014137ff4cSJacques Vidrine #define IS_DOT_DOTDOT(X) ((X)[0] == '.' && ((X)[1] == '\0' || \
5024137ff4cSJacques Vidrine 				((X)[1] == '.' && (X)[2] == '\0')))
5034137ff4cSJacques Vidrine 
5048373020dSJacques Vidrine static int
list_files(FILE * out,const char ** files,int n_files,int flags)505b528cefcSMark Murray list_files(FILE *out, const char **files, int n_files, int flags)
506b528cefcSMark Murray {
507b528cefcSMark Murray     struct fileinfo *fi;
508b528cefcSMark Murray     int i;
5094137ff4cSJacques Vidrine     int *dirs = NULL;
5104137ff4cSJacques Vidrine     size_t total_blocks = 0;
5114137ff4cSJacques Vidrine     int n_print = 0;
5128373020dSJacques Vidrine     int ret = 0;
5138373020dSJacques Vidrine 
5148373020dSJacques Vidrine     if(n_files == 0)
5158373020dSJacques Vidrine 	return 0;
5164137ff4cSJacques Vidrine 
5174137ff4cSJacques Vidrine     if(n_files > 1)
5184137ff4cSJacques Vidrine 	flags |= LS_SHOW_DIRNAME;
519b528cefcSMark Murray 
520b528cefcSMark Murray     fi = calloc(n_files, sizeof(*fi));
521b528cefcSMark Murray     if (fi == NULL) {
5228373020dSJacques Vidrine 	syslog(LOG_ERR, "out of memory");
5238373020dSJacques Vidrine 	return -1;
524b528cefcSMark Murray     }
525b528cefcSMark Murray     for(i = 0; i < n_files; i++) {
526b528cefcSMark Murray 	if(lstat_file(files[i], &fi[i].st) < 0) {
527b528cefcSMark Murray 	    sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
528b528cefcSMark Murray 	    fi[i].filename = NULL;
529b528cefcSMark Murray 	} else {
5304137ff4cSJacques Vidrine 	    int include_in_list = 1;
5314137ff4cSJacques Vidrine 	    total_blocks += block_convert(fi[i].st.st_blocks);
5324137ff4cSJacques Vidrine 	    if(S_ISDIR(fi[i].st.st_mode)) {
5334137ff4cSJacques Vidrine 		if(dirs == NULL)
5344137ff4cSJacques Vidrine 		    dirs = calloc(n_files, sizeof(*dirs));
5354137ff4cSJacques Vidrine 		if(dirs == NULL) {
5368373020dSJacques Vidrine 		    syslog(LOG_ERR, "%s: %m", files[i]);
5378373020dSJacques Vidrine 		    ret = -1;
5384137ff4cSJacques Vidrine 		    goto out;
5394137ff4cSJacques Vidrine 		}
5404137ff4cSJacques Vidrine 		dirs[i] = 1;
5414137ff4cSJacques Vidrine 		if((flags & LS_DIRS) == 0)
5424137ff4cSJacques Vidrine 		    include_in_list = 0;
5434137ff4cSJacques Vidrine 	    }
5444137ff4cSJacques Vidrine 	    if(include_in_list) {
545c19800e8SDoug Rabson 		ret = make_fileinfo(out, files[i], &fi[i], flags);
546c19800e8SDoug Rabson 		if (ret)
547c19800e8SDoug Rabson 		    goto out;
5484137ff4cSJacques Vidrine 		n_print++;
549b528cefcSMark Murray 	    }
550b528cefcSMark Murray 	}
551b528cefcSMark Murray     }
552b528cefcSMark Murray     switch(SORT_MODE(flags)) {
553b528cefcSMark Murray     case LS_SORT_NAME:
554b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
555b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_filename);
556b528cefcSMark Murray 	break;
557b528cefcSMark Murray     case LS_SORT_MTIME:
558b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
559b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_mtime);
560b528cefcSMark Murray 	break;
561b528cefcSMark Murray     case LS_SORT_SIZE:
562b528cefcSMark Murray 	qsort(fi, n_files, sizeof(*fi),
563b528cefcSMark Murray 	      (int (*)(const void*, const void*))compare_size);
564b528cefcSMark Murray 	break;
565b528cefcSMark Murray     }
5665e9cd1aeSAssar Westerlund     if(DISP_MODE(flags) == LS_DISP_LONG) {
567b528cefcSMark Murray 	int max_inode = 0;
568b528cefcSMark Murray 	int max_bsize = 0;
569b528cefcSMark Murray 	int max_n_link = 0;
570b528cefcSMark Murray 	int max_user = 0;
571b528cefcSMark Murray 	int max_group = 0;
572b528cefcSMark Murray 	int max_size = 0;
573b528cefcSMark Murray 	int max_major = 0;
574b528cefcSMark Murray 	int max_minor = 0;
575b528cefcSMark Murray 	int max_date = 0;
576b528cefcSMark Murray 	for(i = 0; i < n_files; i++) {
577b528cefcSMark Murray 	    if(fi[i].filename == NULL)
578b528cefcSMark Murray 		continue;
579b528cefcSMark Murray 	    if(fi[i].inode > max_inode)
580b528cefcSMark Murray 		max_inode = fi[i].inode;
581b528cefcSMark Murray 	    if(fi[i].bsize > max_bsize)
582b528cefcSMark Murray 		max_bsize = fi[i].bsize;
583b528cefcSMark Murray 	    if(fi[i].n_link > max_n_link)
584b528cefcSMark Murray 		max_n_link = fi[i].n_link;
585b528cefcSMark Murray 	    if(strlen(fi[i].user) > max_user)
586b528cefcSMark Murray 		max_user = strlen(fi[i].user);
587b528cefcSMark Murray 	    if(strlen(fi[i].group) > max_group)
588b528cefcSMark Murray 		max_group = strlen(fi[i].group);
589b528cefcSMark Murray 	    if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
590b528cefcSMark Murray 		max_major = strlen(fi[i].major);
591b528cefcSMark Murray 	    if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
592b528cefcSMark Murray 		max_minor = strlen(fi[i].minor);
593b528cefcSMark Murray 	    if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
594b528cefcSMark Murray 		max_size = strlen(fi[i].size);
595b528cefcSMark Murray 	    if(strlen(fi[i].date) > max_date)
596b528cefcSMark Murray 		max_date = strlen(fi[i].date);
597b528cefcSMark Murray 	}
598b528cefcSMark Murray 	if(max_size < max_major + max_minor + 2)
599b528cefcSMark Murray 	    max_size = max_major + max_minor + 2;
600b528cefcSMark Murray 	else if(max_size - max_minor - 2 > max_major)
601b528cefcSMark Murray 	    max_major = max_size - max_minor - 2;
602c19800e8SDoug Rabson 	max_inode = find_log10(max_inode);
603c19800e8SDoug Rabson 	max_bsize = find_log10(max_bsize);
604c19800e8SDoug Rabson 	max_n_link = find_log10(max_n_link);
605b528cefcSMark Murray 
6064137ff4cSJacques Vidrine 	if(n_print > 0)
6074137ff4cSJacques Vidrine 	    sec_fprintf2(out, "total %lu\r\n", (unsigned long)total_blocks);
608b528cefcSMark Murray 	if(flags & LS_SORT_REVERSE)
609b528cefcSMark Murray 	    for(i = n_files - 1; i >= 0; i--)
610b528cefcSMark Murray 		print_file(out,
611b528cefcSMark Murray 			   flags,
612b528cefcSMark Murray 			   &fi[i],
613b528cefcSMark Murray 			   max_inode,
614b528cefcSMark Murray 			   max_bsize,
615b528cefcSMark Murray 			   max_n_link,
616b528cefcSMark Murray 			   max_user,
617b528cefcSMark Murray 			   max_group,
618b528cefcSMark Murray 			   max_size,
619b528cefcSMark Murray 			   max_major,
620b528cefcSMark Murray 			   max_minor,
621b528cefcSMark Murray 			   max_date);
622b528cefcSMark Murray 	else
623b528cefcSMark Murray 	    for(i = 0; i < n_files; i++)
624b528cefcSMark Murray 		print_file(out,
625b528cefcSMark Murray 			   flags,
626b528cefcSMark Murray 			   &fi[i],
627b528cefcSMark Murray 			   max_inode,
628b528cefcSMark Murray 			   max_bsize,
629b528cefcSMark Murray 			   max_n_link,
630b528cefcSMark Murray 			   max_user,
631b528cefcSMark Murray 			   max_group,
632b528cefcSMark Murray 			   max_size,
633b528cefcSMark Murray 			   max_major,
634b528cefcSMark Murray 			   max_minor,
635b528cefcSMark Murray 			   max_date);
6365e9cd1aeSAssar Westerlund     } else if(DISP_MODE(flags) == LS_DISP_COLUMN ||
6375e9cd1aeSAssar Westerlund 	      DISP_MODE(flags) == LS_DISP_CROSS) {
6385e9cd1aeSAssar Westerlund 	int max_len = 0;
6394137ff4cSJacques Vidrine 	int size_len = 0;
6405e9cd1aeSAssar Westerlund 	int num_files = n_files;
6415e9cd1aeSAssar Westerlund 	int columns;
6425e9cd1aeSAssar Westerlund 	int j;
6435e9cd1aeSAssar Westerlund 	for(i = 0; i < n_files; i++) {
6445e9cd1aeSAssar Westerlund 	    if(fi[i].filename == NULL) {
6455e9cd1aeSAssar Westerlund 		num_files--;
6465e9cd1aeSAssar Westerlund 		continue;
6475e9cd1aeSAssar Westerlund 	    }
6485e9cd1aeSAssar Westerlund 	    if(strlen(fi[i].filename) > max_len)
6495e9cd1aeSAssar Westerlund 		max_len = strlen(fi[i].filename);
650c19800e8SDoug Rabson 	    if(find_log10(fi[i].bsize) > size_len)
651c19800e8SDoug Rabson 		size_len = find_log10(fi[i].bsize);
6525e9cd1aeSAssar Westerlund 	}
6534137ff4cSJacques Vidrine 	if(num_files == 0)
6544137ff4cSJacques Vidrine 	    goto next;
6554137ff4cSJacques Vidrine 	if(flags & LS_SIZE) {
6564137ff4cSJacques Vidrine 	    columns = 80 / (size_len + 1 + max_len + 1);
6574137ff4cSJacques Vidrine 	    max_len = 80 / columns - size_len - 1;
6584137ff4cSJacques Vidrine 	} else {
6595e9cd1aeSAssar Westerlund 	    columns = 80 / (max_len + 1); /* get space between columns */
6605e9cd1aeSAssar Westerlund 	    max_len = 80 / columns;
6614137ff4cSJacques Vidrine 	}
6624137ff4cSJacques Vidrine 	if(flags & LS_SIZE)
6634137ff4cSJacques Vidrine 	    sec_fprintf2(out, "total %lu\r\n",
6644137ff4cSJacques Vidrine 			 (unsigned long)total_blocks);
6655e9cd1aeSAssar Westerlund 	if(DISP_MODE(flags) == LS_DISP_CROSS) {
6665e9cd1aeSAssar Westerlund 	    for(i = 0, j = 0; i < n_files; i++) {
6675e9cd1aeSAssar Westerlund 		if(fi[i].filename == NULL)
6685e9cd1aeSAssar Westerlund 		    continue;
6694137ff4cSJacques Vidrine 		if(flags & LS_SIZE)
6704137ff4cSJacques Vidrine 		    sec_fprintf2(out, "%*u %-*s", size_len, fi[i].bsize,
6714137ff4cSJacques Vidrine 				 max_len, fi[i].filename);
6724137ff4cSJacques Vidrine 		else
6735e9cd1aeSAssar Westerlund 		    sec_fprintf2(out, "%-*s", max_len, fi[i].filename);
6745e9cd1aeSAssar Westerlund 		j++;
6755e9cd1aeSAssar Westerlund 		if(j == columns) {
6765e9cd1aeSAssar Westerlund 		    sec_fprintf2(out, "\r\n");
6775e9cd1aeSAssar Westerlund 		    j = 0;
6785e9cd1aeSAssar Westerlund 		}
6795e9cd1aeSAssar Westerlund 	    }
6805e9cd1aeSAssar Westerlund 	    if(j > 0)
6815e9cd1aeSAssar Westerlund 		sec_fprintf2(out, "\r\n");
6825e9cd1aeSAssar Westerlund 	} else {
6835e9cd1aeSAssar Westerlund 	    int skip = (num_files + columns - 1) / columns;
684*ae771770SStanislav Sedov 
6855e9cd1aeSAssar Westerlund 	    for(i = 0; i < skip; i++) {
6865e9cd1aeSAssar Westerlund 		for(j = i; j < n_files;) {
6875e9cd1aeSAssar Westerlund 		    while(j < n_files && fi[j].filename == NULL)
6885e9cd1aeSAssar Westerlund 			j++;
6894137ff4cSJacques Vidrine 		    if(flags & LS_SIZE)
6904137ff4cSJacques Vidrine 			sec_fprintf2(out, "%*u %-*s", size_len, fi[j].bsize,
6914137ff4cSJacques Vidrine 				     max_len, fi[j].filename);
6924137ff4cSJacques Vidrine 		    else
6935e9cd1aeSAssar Westerlund 			sec_fprintf2(out, "%-*s", max_len, fi[j].filename);
6945e9cd1aeSAssar Westerlund 		    j += skip;
6955e9cd1aeSAssar Westerlund 		}
6965e9cd1aeSAssar Westerlund 		sec_fprintf2(out, "\r\n");
6975e9cd1aeSAssar Westerlund 	    }
6985e9cd1aeSAssar Westerlund 	}
6995e9cd1aeSAssar Westerlund     } else {
7005e9cd1aeSAssar Westerlund 	for(i = 0; i < n_files; i++) {
7015e9cd1aeSAssar Westerlund 	    if(fi[i].filename == NULL)
7025e9cd1aeSAssar Westerlund 		continue;
7035e9cd1aeSAssar Westerlund 	    sec_fprintf2(out, "%s\r\n", fi[i].filename);
7045e9cd1aeSAssar Westerlund 	}
7055e9cd1aeSAssar Westerlund     }
7064137ff4cSJacques Vidrine  next:
7074137ff4cSJacques Vidrine     if(((flags & LS_DIRS) == 0 || (flags & LS_RECURSIVE)) && dirs != NULL) {
7084137ff4cSJacques Vidrine 	for(i = 0; i < n_files; i++) {
7094137ff4cSJacques Vidrine 	    if(dirs[i]) {
7104137ff4cSJacques Vidrine 		const char *p = strrchr(files[i], '/');
7114137ff4cSJacques Vidrine 		if(p == NULL)
7124137ff4cSJacques Vidrine 		    p = files[i];
7134137ff4cSJacques Vidrine 		else
7144137ff4cSJacques Vidrine 		    p++;
7154137ff4cSJacques Vidrine 		if(!(flags & LS_DIR_FLAG) || !IS_DOT_DOTDOT(p)) {
7164137ff4cSJacques Vidrine 		    if((flags & LS_SHOW_DIRNAME)) {
7174137ff4cSJacques Vidrine 			if ((flags & LS_EXTRA_BLANK))
7184137ff4cSJacques Vidrine 			    sec_fprintf2(out, "\r\n");
7194137ff4cSJacques Vidrine 			sec_fprintf2(out, "%s:\r\n", files[i]);
7204137ff4cSJacques Vidrine 		    }
7214137ff4cSJacques Vidrine 		    list_dir(out, files[i], flags | LS_DIRS | LS_EXTRA_BLANK);
7224137ff4cSJacques Vidrine 		}
7234137ff4cSJacques Vidrine 	    }
7244137ff4cSJacques Vidrine 	}
7254137ff4cSJacques Vidrine     }
7264137ff4cSJacques Vidrine  out:
727b528cefcSMark Murray     for(i = 0; i < n_files; i++)
728b528cefcSMark Murray 	free_fileinfo(&fi[i]);
729b528cefcSMark Murray     free(fi);
7304137ff4cSJacques Vidrine     if(dirs != NULL)
7314137ff4cSJacques Vidrine 	free(dirs);
7328373020dSJacques Vidrine     return ret;
733b528cefcSMark Murray }
734b528cefcSMark Murray 
735b528cefcSMark Murray static void
free_files(char ** files,int n)736b528cefcSMark Murray free_files (char **files, int n)
737b528cefcSMark Murray {
738b528cefcSMark Murray     int i;
739b528cefcSMark Murray 
740b528cefcSMark Murray     for (i = 0; i < n; ++i)
741b528cefcSMark Murray 	free (files[i]);
742b528cefcSMark Murray     free (files);
743b528cefcSMark Murray }
744b528cefcSMark Murray 
7454137ff4cSJacques Vidrine static int
hide_file(const char * filename,int flags)7464137ff4cSJacques Vidrine hide_file(const char *filename, int flags)
7474137ff4cSJacques Vidrine {
7484137ff4cSJacques Vidrine     if(filename[0] != '.')
7494137ff4cSJacques Vidrine 	return 0;
7504137ff4cSJacques Vidrine     if((flags & LS_IGNORE_DOT))
7514137ff4cSJacques Vidrine 	return 1;
7524137ff4cSJacques Vidrine     if(filename[1] == '\0' || (filename[1] == '.' && filename[2] == '\0')) {
7534137ff4cSJacques Vidrine 	if((flags & LS_SHOW_ALL))
7544137ff4cSJacques Vidrine 	    return 0;
7554137ff4cSJacques Vidrine 	else
7564137ff4cSJacques Vidrine 	    return 1;
7574137ff4cSJacques Vidrine     }
7584137ff4cSJacques Vidrine     return 0;
7594137ff4cSJacques Vidrine }
7604137ff4cSJacques Vidrine 
7618373020dSJacques Vidrine static int
list_dir(FILE * out,const char * directory,int flags)762b528cefcSMark Murray list_dir(FILE *out, const char *directory, int flags)
763b528cefcSMark Murray {
764b528cefcSMark Murray     DIR *d = opendir(directory);
765b528cefcSMark Murray     struct dirent *ent;
766b528cefcSMark Murray     char **files = NULL;
767b528cefcSMark Murray     int n_files = 0;
768c19800e8SDoug Rabson     int ret;
769b528cefcSMark Murray 
770b528cefcSMark Murray     if(d == NULL) {
7718373020dSJacques Vidrine 	syslog(LOG_ERR, "%s: %m", directory);
7728373020dSJacques Vidrine 	return -1;
773b528cefcSMark Murray     }
774b528cefcSMark Murray     while((ent = readdir(d)) != NULL) {
775b528cefcSMark Murray 	void *tmp;
776b528cefcSMark Murray 
7774137ff4cSJacques Vidrine 	if(hide_file(ent->d_name, flags))
778b528cefcSMark Murray 	    continue;
779b528cefcSMark Murray 	tmp = realloc(files, (n_files + 1) * sizeof(*files));
780b528cefcSMark Murray 	if (tmp == NULL) {
7818373020dSJacques Vidrine 	    syslog(LOG_ERR, "%s: out of memory", directory);
782b528cefcSMark Murray 	    free_files (files, n_files);
783b528cefcSMark Murray 	    closedir (d);
7848373020dSJacques Vidrine 	    return -1;
785b528cefcSMark Murray 	}
786b528cefcSMark Murray 	files = tmp;
787c19800e8SDoug Rabson 	ret = asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
788c19800e8SDoug Rabson 	if (ret == -1) {
7898373020dSJacques Vidrine 	    syslog(LOG_ERR, "%s: out of memory", directory);
790b528cefcSMark Murray 	    free_files (files, n_files);
791b528cefcSMark Murray 	    closedir (d);
7928373020dSJacques Vidrine 	    return -1;
793b528cefcSMark Murray 	}
794b528cefcSMark Murray 	++n_files;
795b528cefcSMark Murray     }
796b528cefcSMark Murray     closedir(d);
7978373020dSJacques Vidrine     return list_files(out, (const char**)files, n_files, flags | LS_DIR_FLAG);
798b528cefcSMark Murray }
799b528cefcSMark Murray 
8004137ff4cSJacques Vidrine static int
parse_flags(const char * options)8014137ff4cSJacques Vidrine parse_flags(const char *options)
802b528cefcSMark Murray {
8034137ff4cSJacques Vidrine #ifdef TEST
8044137ff4cSJacques Vidrine     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_COLUMN;
8054137ff4cSJacques Vidrine #else
8065e9cd1aeSAssar Westerlund     int flags = LS_SORT_NAME | LS_IGNORE_DOT | LS_DISP_LONG;
8074137ff4cSJacques Vidrine #endif
808b528cefcSMark Murray 
809b528cefcSMark Murray     const char *p;
8104137ff4cSJacques Vidrine     if(options == NULL || *options != '-')
8114137ff4cSJacques Vidrine 	return flags;
8124137ff4cSJacques Vidrine     for(p = options + 1; *p; p++) {
813b528cefcSMark Murray 	switch(*p) {
8145e9cd1aeSAssar Westerlund 	case '1':
8155e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE);
8165e9cd1aeSAssar Westerlund 	    break;
817b528cefcSMark Murray 	case 'a':
8184137ff4cSJacques Vidrine 	    flags |= LS_SHOW_ALL;
8194137ff4cSJacques Vidrine 	    /*FALLTHROUGH*/
820b528cefcSMark Murray 	case 'A':
821b528cefcSMark Murray 	    flags &= ~LS_IGNORE_DOT;
822b528cefcSMark Murray 	    break;
823b528cefcSMark Murray 	case 'C':
8245e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_COLUMN;
825b528cefcSMark Murray 	    break;
826b528cefcSMark Murray 	case 'd':
827b528cefcSMark Murray 	    flags |= LS_DIRS;
828b528cefcSMark Murray 	    break;
829b528cefcSMark Murray 	case 'f':
830b528cefcSMark Murray 	    flags = (flags & ~LS_SORT_MODE);
831b528cefcSMark Murray 	    break;
8325e9cd1aeSAssar Westerlund 	case 'F':
8335e9cd1aeSAssar Westerlund 	    flags |= LS_TYPE;
8345e9cd1aeSAssar Westerlund 	    break;
835b528cefcSMark Murray 	case 'i':
8365e9cd1aeSAssar Westerlund 	    flags |= LS_INODE;
837b528cefcSMark Murray 	    break;
838b528cefcSMark Murray 	case 'l':
8395e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_LONG;
840b528cefcSMark Murray 	    break;
8414137ff4cSJacques Vidrine 	case 'r':
8424137ff4cSJacques Vidrine 	    flags |= LS_SORT_REVERSE;
8434137ff4cSJacques Vidrine 	    break;
8444137ff4cSJacques Vidrine 	case 'R':
8454137ff4cSJacques Vidrine 	    flags |= LS_RECURSIVE;
846b528cefcSMark Murray 	    break;
847b528cefcSMark Murray 	case 's':
848b528cefcSMark Murray 	    flags |= LS_SIZE;
849b528cefcSMark Murray 	    break;
850b528cefcSMark Murray 	case 'S':
851b528cefcSMark Murray 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
852b528cefcSMark Murray 	    break;
8534137ff4cSJacques Vidrine 	case 't':
8544137ff4cSJacques Vidrine 	    flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
855b528cefcSMark Murray 	    break;
8565e9cd1aeSAssar Westerlund 	case 'x':
8575e9cd1aeSAssar Westerlund 	    flags = (flags & ~LS_DISP_MODE) | LS_DISP_CROSS;
8585e9cd1aeSAssar Westerlund 	    break;
8594137ff4cSJacques Vidrine 	    /* these are a bunch of unimplemented flags from BSD ls */
8604137ff4cSJacques Vidrine 	case 'k': /* display sizes in kB */
8614137ff4cSJacques Vidrine 	case 'c': /* last change time */
8624137ff4cSJacques Vidrine 	case 'L': /* list symlink target */
8634137ff4cSJacques Vidrine 	case 'm': /* stream output */
8644137ff4cSJacques Vidrine 	case 'o': /* BSD file flags */
8654137ff4cSJacques Vidrine 	case 'p': /* display / after directories */
8664137ff4cSJacques Vidrine 	case 'q': /* print non-graphic characters */
8674137ff4cSJacques Vidrine 	case 'u': /* use last access time */
8684137ff4cSJacques Vidrine 	case 'T': /* display complete time */
8694137ff4cSJacques Vidrine 	case 'W': /* include whiteouts */
8704137ff4cSJacques Vidrine 	    break;
871b528cefcSMark Murray 	}
872b528cefcSMark Murray     }
8734137ff4cSJacques Vidrine     return flags;
8744137ff4cSJacques Vidrine }
8754137ff4cSJacques Vidrine 
8768373020dSJacques Vidrine int
builtin_ls(FILE * out,const char * file)8774137ff4cSJacques Vidrine builtin_ls(FILE *out, const char *file)
8784137ff4cSJacques Vidrine {
8794137ff4cSJacques Vidrine     int flags;
8808373020dSJacques Vidrine     int ret;
8814137ff4cSJacques Vidrine 
8824137ff4cSJacques Vidrine     if(*file == '-') {
8834137ff4cSJacques Vidrine 	flags = parse_flags(file);
884b528cefcSMark Murray 	file = ".";
8854137ff4cSJacques Vidrine     } else
8864137ff4cSJacques Vidrine 	flags = parse_flags("");
8874137ff4cSJacques Vidrine 
8888373020dSJacques Vidrine     ret = list_files(out, &file, 1, flags);
889b528cefcSMark Murray     sec_fflush(out);
8908373020dSJacques Vidrine     return ret;
891b528cefcSMark Murray }
892