2 * Copyright (c) 1999 - 2000 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 * used to endorse or promote products derived from this software without
19 * specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
33 #include "ftpd_locl.h"
35 RCSID("$Id: ls.c,v 1.13.2.2 2000/06/23 02:51:09 assar Exp $");
54 #define LS_IGNORE_DOT 2
55 #define LS_SORT_MODE 12
56 #define SORT_MODE(f) ((f) & LS_SORT_MODE)
57 #define LS_SORT_NAME 4
58 #define LS_SORT_MTIME 8
59 #define LS_SORT_SIZE 12
60 #define LS_SORT_REVERSE 16
66 #define S_ISTXT S_ISVTX
70 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK)
74 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK)
78 make_fileinfo(const char *filename, struct fileinfo *file, int flags)
81 struct stat *st = &file->st;
83 file->inode = st->st_ino;
85 file->bsize = st->st_blocks * S_BLKSIZE / 1024;
87 file->bsize = st->st_blocks * 512 / 1024;
90 if(S_ISDIR(st->st_mode))
92 else if(S_ISCHR(st->st_mode))
94 else if(S_ISBLK(st->st_mode))
96 else if(S_ISREG(st->st_mode))
98 else if(S_ISFIFO(st->st_mode))
100 else if(S_ISLNK(st->st_mode))
102 else if(S_ISSOCK(st->st_mode))
105 else if(S_ISWHT(st->st_mode))
111 char *x[] = { "---", "--x", "-w-", "-wx",
112 "r--", "r-x", "rw-", "rwx" };
113 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]);
114 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]);
115 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]);
116 if((st->st_mode & S_ISUID)) {
117 if((st->st_mode & S_IXUSR))
122 if((st->st_mode & S_ISGID)) {
123 if((st->st_mode & S_IXGRP))
128 if((st->st_mode & S_ISTXT)) {
129 if((st->st_mode & S_IXOTH))
135 file->n_link = st->st_nlink;
138 pwd = getpwuid(st->st_uid);
140 asprintf(&file->user, "%u", (unsigned)st->st_uid);
142 file->user = strdup(pwd->pw_name);
146 grp = getgrgid(st->st_gid);
148 asprintf(&file->group, "%u", (unsigned)st->st_gid);
150 file->group = strdup(grp->gr_name);
153 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
154 #if defined(major) && defined(minor)
155 asprintf(&file->major, "%u", (unsigned)major(st->st_rdev));
156 asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev));
158 /* Don't want to use the DDI/DKI crap. */
159 asprintf(&file->major, "%u", (unsigned)st->st_rdev);
160 asprintf(&file->minor, "%u", 0);
163 asprintf(&file->size, "%lu", (unsigned long)st->st_size);
166 time_t t = time(NULL);
167 time_t mtime = st->st_mtime;
168 struct tm *tm = localtime(&mtime);
169 if((t - mtime > 6*30*24*60*60) ||
170 (mtime - t > 6*30*24*60*60))
171 strftime(buf, sizeof(buf), "%b %e %Y", tm);
173 strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
174 file->date = strdup(buf);
177 const char *p = strrchr(filename, '/');
182 file->filename = strdup(p);
184 if(S_ISLNK(st->st_mode)) {
186 n = readlink((char *)filename, buf, sizeof(buf));
189 file->link = strdup(buf);
191 warn("%s: readlink", filename);
196 print_file(FILE *out,
209 if(f->filename == NULL)
212 if(flags & LS_INODE) {
213 sec_fprintf2(out, "%*d", max_inode, f->inode);
214 sec_fprintf2(out, " ");
216 if(flags & LS_SIZE) {
217 sec_fprintf2(out, "%*d", max_bsize, f->bsize);
218 sec_fprintf2(out, " ");
220 sec_fprintf2(out, "%s", f->mode);
221 sec_fprintf2(out, " ");
222 sec_fprintf2(out, "%*d", max_n_link, f->n_link);
223 sec_fprintf2(out, " ");
224 sec_fprintf2(out, "%-*s", max_user, f->user);
225 sec_fprintf2(out, " ");
226 sec_fprintf2(out, "%-*s", max_group, f->group);
227 sec_fprintf2(out, " ");
228 if(f->major != NULL && f->minor != NULL)
229 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor);
231 sec_fprintf2(out, "%*s", max_size, f->size);
232 sec_fprintf2(out, " ");
233 sec_fprintf2(out, "%*s", max_date, f->date);
234 sec_fprintf2(out, " ");
235 sec_fprintf2(out, "%s", f->filename);
237 sec_fprintf2(out, " -> %s", f->link);
238 sec_fprintf2(out, "\r\n");
242 compare_filename(struct fileinfo *a, struct fileinfo *b)
244 if(a->filename == NULL)
246 if(b->filename == NULL)
248 return strcmp(a->filename, b->filename);
252 compare_mtime(struct fileinfo *a, struct fileinfo *b)
254 if(a->filename == NULL)
256 if(b->filename == NULL)
258 return a->st.st_mtime - b->st.st_mtime;
262 compare_size(struct fileinfo *a, struct fileinfo *b)
264 if(a->filename == NULL)
266 if(b->filename == NULL)
268 return a->st.st_size - b->st.st_size;
272 list_dir(FILE *out, const char *directory, int flags);
286 * Operate as lstat but fake up entries for AFS mount points so we don't
287 * have to fetch them.
291 lstat_file (const char *file, struct stat *sb)
296 && strcmp(file, ".."))
298 struct ViceIoctl a_params;
301 static ino_t ino_counter = 0, ino_last = 0;
303 const int maxsize = 2048;
305 path_bkp = strdup (file);
306 if (path_bkp == NULL)
309 a_params.out = malloc (maxsize);
310 if (a_params.out == NULL) {
315 /* If path contains more than the filename alone - split it */
317 last = strrchr (path_bkp, '/');
320 a_params.in = last + 1;
322 a_params.in = (char *)file;
324 a_params.in_size = strlen (a_params.in) + 1;
325 a_params.out_size = maxsize;
327 ret = k_pioctl (last ? path_bkp : "." ,
328 VIOC_AFS_STAT_MT_PT, &a_params, 0);
336 /* if we get EINVAL this is probably not a mountpoint */
337 return lstat (file, sb);
341 * wow this was a mountpoint, lets cook the struct stat
342 * use . as a prototype
345 ret = lstat (path_bkp, sb);
350 if (ino_last == sb->st_ino)
353 ino_last = sb->st_ino;
356 sb->st_ino += ino_counter;
362 return lstat (file, sb);
366 list_files(FILE *out, char **files, int n_files, int flags)
371 fi = calloc(n_files, sizeof(*fi));
373 sec_fprintf2(out, "ouf of memory\r\n");
376 for(i = 0; i < n_files; i++) {
377 if(lstat_file(files[i], &fi[i].st) < 0) {
378 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno));
379 fi[i].filename = NULL;
381 if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) {
383 sec_fprintf2(out, "%s:\r\n", files[i]);
384 list_dir(out, files[i], flags);
386 make_fileinfo(files[i], &fi[i], flags);
390 switch(SORT_MODE(flags)) {
392 qsort(fi, n_files, sizeof(*fi),
393 (int (*)(const void*, const void*))compare_filename);
396 qsort(fi, n_files, sizeof(*fi),
397 (int (*)(const void*, const void*))compare_mtime);
400 qsort(fi, n_files, sizeof(*fi),
401 (int (*)(const void*, const void*))compare_size);
414 for(i = 0; i < n_files; i++) {
415 if(fi[i].filename == NULL)
417 if(fi[i].inode > max_inode)
418 max_inode = fi[i].inode;
419 if(fi[i].bsize > max_bsize)
420 max_bsize = fi[i].bsize;
421 if(fi[i].n_link > max_n_link)
422 max_n_link = fi[i].n_link;
423 if(strlen(fi[i].user) > max_user)
424 max_user = strlen(fi[i].user);
425 if(strlen(fi[i].group) > max_group)
426 max_group = strlen(fi[i].group);
427 if(fi[i].major != NULL && strlen(fi[i].major) > max_major)
428 max_major = strlen(fi[i].major);
429 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor)
430 max_minor = strlen(fi[i].minor);
431 if(fi[i].size != NULL && strlen(fi[i].size) > max_size)
432 max_size = strlen(fi[i].size);
433 if(strlen(fi[i].date) > max_date)
434 max_date = strlen(fi[i].date);
436 if(max_size < max_major + max_minor + 2)
437 max_size = max_major + max_minor + 2;
438 else if(max_size - max_minor - 2 > max_major)
439 max_major = max_size - max_minor - 2;
440 max_inode = log10(max_inode);
441 max_bsize = log10(max_bsize);
442 max_n_link = log10(max_n_link);
444 if(flags & LS_SORT_REVERSE)
445 for(i = n_files - 1; i >= 0; i--)
459 for(i = 0; i < n_files; i++)
476 free_files (char **files, int n)
480 for (i = 0; i < n; ++i)
486 list_dir(FILE *out, const char *directory, int flags)
488 DIR *d = opendir(directory);
494 sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno));
497 while((ent = readdir(d)) != NULL) {
500 if(ent->d_name[0] == '.') {
501 if (flags & LS_IGNORE_DOT)
503 if (ent->d_name[1] == 0) /* Ignore . */
505 if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */
508 tmp = realloc(files, (n_files + 1) * sizeof(*files));
510 sec_fprintf2(out, "%s: out of memory\r\n", directory);
511 free_files (files, n_files);
516 asprintf(&files[n_files], "%s/%s", directory, ent->d_name);
517 if (files[n_files] == NULL) {
518 sec_fprintf2(out, "%s: out of memory\r\n", directory);
519 free_files (files, n_files);
526 list_files(out, files, n_files, flags | LS_DIRS);
530 builtin_ls(FILE *out, const char *file)
532 int flags = LS_SORT_NAME;
536 for(p = file + 1; *p; p++) {
540 flags &= ~LS_IGNORE_DOT;
548 flags = (flags & ~LS_SORT_MODE);
551 flags |= flags | LS_INODE;
556 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
562 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
565 flags |= LS_SORT_REVERSE;
571 list_files(out, &file, 1, flags);