Initial import from FreeBSD RELENG_4:
[games.git] / crypto / kerberosIV / appl / ftp / ftpd / ls.c
1 /*
2  * Copyright (c) 1999 - 2000 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
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. 
16  *
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.
20  *
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. */
32
33 #include "ftpd_locl.h"
34
35 RCSID("$Id: ls.c,v 1.13.2.2 2000/06/23 02:51:09 assar Exp $");
36
37 struct fileinfo {
38     struct stat st;
39     int inode;
40     int bsize;
41     char mode[11];
42     int n_link;
43     char *user;
44     char *group;
45     char *size;
46     char *major;
47     char *minor;
48     char *date;
49     char *filename;
50     char *link;
51 };
52
53 #define LS_DIRS          1
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
61
62 #define LS_SIZE         32
63 #define LS_INODE        64
64
65 #ifndef S_ISTXT
66 #define S_ISTXT S_ISVTX
67 #endif
68
69 #ifndef S_ISSOCK
70 #define S_ISSOCK(mode)  (((mode) & _S_IFMT) == S_IFSOCK)
71 #endif
72
73 #ifndef S_ISLNK
74 #define S_ISLNK(mode)   (((mode) & _S_IFMT) == S_IFLNK)
75 #endif
76
77 static void
78 make_fileinfo(const char *filename, struct fileinfo *file, int flags)
79 {
80     char buf[128];
81     struct stat *st = &file->st;
82
83     file->inode = st->st_ino;
84 #ifdef S_BLKSIZE
85     file->bsize = st->st_blocks * S_BLKSIZE / 1024;
86 #else
87     file->bsize = st->st_blocks * 512 / 1024;
88 #endif
89
90     if(S_ISDIR(st->st_mode))
91         file->mode[0] = 'd';
92     else if(S_ISCHR(st->st_mode))
93         file->mode[0] = 'c';
94     else if(S_ISBLK(st->st_mode))
95         file->mode[0] = 'b';
96     else if(S_ISREG(st->st_mode))
97         file->mode[0] = '-';
98     else if(S_ISFIFO(st->st_mode))
99         file->mode[0] = 'p';
100     else if(S_ISLNK(st->st_mode))
101         file->mode[0] = 'l';
102     else if(S_ISSOCK(st->st_mode))
103         file->mode[0] = 's';
104 #ifdef S_ISWHT
105     else if(S_ISWHT(st->st_mode))
106         file->mode[0] = 'w';
107 #endif
108     else 
109         file->mode[0] = '?';
110     {
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))
118                 file->mode[3] = 's';
119             else
120                 file->mode[3] = 'S';
121         }
122         if((st->st_mode & S_ISGID)) {
123             if((st->st_mode & S_IXGRP))
124                 file->mode[6] = 's';
125             else
126                 file->mode[6] = 'S';
127         }
128         if((st->st_mode & S_ISTXT)) {
129             if((st->st_mode & S_IXOTH))
130                 file->mode[9] = 't';
131             else
132                 file->mode[9] = 'T';
133         }
134     }
135     file->n_link = st->st_nlink;
136     {
137         struct passwd *pwd;
138         pwd = getpwuid(st->st_uid);
139         if(pwd == NULL)
140             asprintf(&file->user, "%u", (unsigned)st->st_uid);
141         else
142             file->user = strdup(pwd->pw_name);
143     }
144     {
145         struct group *grp;
146         grp = getgrgid(st->st_gid);
147         if(grp == NULL)
148             asprintf(&file->group, "%u", (unsigned)st->st_gid);
149         else
150             file->group = strdup(grp->gr_name);
151     }
152     
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));
157 #else
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);
161 #endif
162     } else
163         asprintf(&file->size, "%lu", (unsigned long)st->st_size);
164
165     {
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);
172         else
173             strftime(buf, sizeof(buf), "%b %e %H:%M", tm);
174         file->date = strdup(buf);
175     }
176     {
177         const char *p = strrchr(filename, '/');
178         if(p)
179             p++;
180         else
181             p = filename;
182         file->filename = strdup(p);
183     }
184     if(S_ISLNK(st->st_mode)) {
185         int n;
186         n = readlink((char *)filename, buf, sizeof(buf));
187         if(n >= 0) {
188             buf[n] = '\0';
189             file->link = strdup(buf);
190         } else
191             warn("%s: readlink", filename);
192     }
193 }
194
195 static void
196 print_file(FILE *out,
197            int flags,
198            struct fileinfo *f,
199            int max_inode,
200            int max_bsize,
201            int max_n_link,
202            int max_user,
203            int max_group,
204            int max_size,
205            int max_major,
206            int max_minor,
207            int max_date)
208 {
209     if(f->filename == NULL)
210         return;
211
212     if(flags & LS_INODE) {
213         sec_fprintf2(out, "%*d", max_inode, f->inode);
214         sec_fprintf2(out, "  ");
215     }
216     if(flags & LS_SIZE) {
217         sec_fprintf2(out, "%*d", max_bsize, f->bsize);
218         sec_fprintf2(out, "  ");
219     }
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);
230     else
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);
236     if(f->link)
237         sec_fprintf2(out, " -> %s", f->link);
238     sec_fprintf2(out, "\r\n");
239 }
240
241 static int
242 compare_filename(struct fileinfo *a, struct fileinfo *b)
243 {
244     if(a->filename == NULL)
245         return 1;
246     if(b->filename == NULL)
247         return -1;
248     return strcmp(a->filename, b->filename);
249 }
250
251 static int
252 compare_mtime(struct fileinfo *a, struct fileinfo *b)
253 {
254     if(a->filename == NULL)
255         return 1;
256     if(b->filename == NULL)
257         return -1;
258     return a->st.st_mtime - b->st.st_mtime;
259 }
260
261 static int
262 compare_size(struct fileinfo *a, struct fileinfo *b)
263 {
264     if(a->filename == NULL)
265         return 1;
266     if(b->filename == NULL)
267         return -1;
268     return a->st.st_size - b->st.st_size;
269 }
270
271 static void
272 list_dir(FILE *out, const char *directory, int flags);
273
274 static int
275 log10(int num)
276 {
277     int i = 1;
278     while(num > 10) {
279         i++;
280         num /= 10;
281     }
282     return i;
283 }
284
285 /*
286  * Operate as lstat but fake up entries for AFS mount points so we don't
287  * have to fetch them.
288  */
289
290 static int
291 lstat_file (const char *file, struct stat *sb)
292 {
293 #ifdef KRB4
294     if (k_hasafs() 
295         && strcmp(file, ".")
296         && strcmp(file, "..")) 
297     {
298         struct ViceIoctl    a_params;
299         char               *last;
300         char               *path_bkp;
301         static ino_t       ino_counter = 0, ino_last = 0;
302         int                ret;
303         const int          maxsize = 2048;
304         
305         path_bkp = strdup (file);
306         if (path_bkp == NULL)
307             return -1;
308         
309         a_params.out = malloc (maxsize);
310         if (a_params.out == NULL) { 
311             free (path_bkp);
312             return -1;
313         }
314         
315         /* If path contains more than the filename alone - split it */
316         
317         last = strrchr (path_bkp, '/');
318         if (last != NULL) {
319             *last = '\0';
320             a_params.in = last + 1;
321         } else
322             a_params.in = (char *)file;
323         
324         a_params.in_size  = strlen (a_params.in) + 1;
325         a_params.out_size = maxsize;
326         
327         ret = k_pioctl (last ? path_bkp : "." ,
328                         VIOC_AFS_STAT_MT_PT, &a_params, 0);
329         free (a_params.out);
330         if (ret < 0) {
331             free (path_bkp);
332
333             if (errno != EINVAL)
334                 return ret;
335             else
336                 /* if we get EINVAL this is probably not a mountpoint */
337                 return lstat (file, sb);
338         }
339
340         /* 
341          * wow this was a mountpoint, lets cook the struct stat
342          * use . as a prototype
343          */
344
345         ret = lstat (path_bkp, sb);
346         free (path_bkp);
347         if (ret < 0)
348             return ret;
349
350         if (ino_last == sb->st_ino)
351             ino_counter++;
352         else {
353             ino_last    = sb->st_ino;
354             ino_counter = 0;
355         }
356         sb->st_ino += ino_counter;
357         sb->st_nlink = 3;
358
359         return 0;
360     }
361 #endif /* KRB4 */
362     return lstat (file, sb);
363 }
364
365 static void
366 list_files(FILE *out, char **files, int n_files, int flags)
367 {
368     struct fileinfo *fi;
369     int i;
370
371     fi = calloc(n_files, sizeof(*fi));
372     if (fi == NULL) {
373         sec_fprintf2(out, "ouf of memory\r\n");
374         return;
375     }
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;
380         } else {
381             if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) {
382                 if(n_files > 1)
383                     sec_fprintf2(out, "%s:\r\n", files[i]);
384                 list_dir(out, files[i], flags);
385             } else {
386                 make_fileinfo(files[i], &fi[i], flags);
387             }
388         }
389     }
390     switch(SORT_MODE(flags)) {
391     case LS_SORT_NAME:
392         qsort(fi, n_files, sizeof(*fi), 
393               (int (*)(const void*, const void*))compare_filename);
394         break;
395     case LS_SORT_MTIME:
396         qsort(fi, n_files, sizeof(*fi), 
397               (int (*)(const void*, const void*))compare_mtime);
398         break;
399     case LS_SORT_SIZE:
400         qsort(fi, n_files, sizeof(*fi), 
401               (int (*)(const void*, const void*))compare_size);
402         break;
403     }
404     {
405         int max_inode = 0;
406         int max_bsize = 0;
407         int max_n_link = 0;
408         int max_user = 0;
409         int max_group = 0;
410         int max_size = 0;
411         int max_major = 0;
412         int max_minor = 0;
413         int max_date = 0;
414         for(i = 0; i < n_files; i++) {
415             if(fi[i].filename == NULL)
416                 continue;
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);
435         }
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);
443
444         if(flags & LS_SORT_REVERSE)
445             for(i = n_files - 1; i >= 0; i--)
446                 print_file(out,
447                            flags,
448                            &fi[i],
449                            max_inode,
450                            max_bsize,
451                            max_n_link,
452                            max_user,
453                            max_group,
454                            max_size,
455                            max_major,
456                            max_minor,
457                            max_date);
458         else
459             for(i = 0; i < n_files; i++)
460                 print_file(out,
461                            flags,
462                            &fi[i],
463                            max_inode,
464                            max_bsize,
465                            max_n_link,
466                            max_user,
467                            max_group,
468                            max_size,
469                            max_major,
470                            max_minor,
471                            max_date);
472     }
473 }
474
475 static void
476 free_files (char **files, int n)
477 {
478     int i;
479
480     for (i = 0; i < n; ++i)
481         free (files[i]);
482     free (files);
483 }
484
485 static void
486 list_dir(FILE *out, const char *directory, int flags)
487 {
488     DIR *d = opendir(directory);
489     struct dirent *ent;
490     char **files = NULL;
491     int n_files = 0;
492
493     if(d == NULL) {
494         sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno));
495         return;
496     }
497     while((ent = readdir(d)) != NULL) {
498         void *tmp;
499
500         if(ent->d_name[0] == '.') {
501             if (flags & LS_IGNORE_DOT)
502                 continue;
503             if (ent->d_name[1] == 0) /* Ignore . */
504                 continue;
505             if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */
506                 continue;
507         }
508         tmp = realloc(files, (n_files + 1) * sizeof(*files));
509         if (tmp == NULL) {
510             sec_fprintf2(out, "%s: out of memory\r\n", directory);
511             free_files (files, n_files);
512             closedir (d);
513             return;
514         }
515         files = tmp;
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);
520             closedir (d);
521             return;
522         }
523         ++n_files;
524     }
525     closedir(d);
526     list_files(out, files, n_files, flags | LS_DIRS);
527 }
528
529 void
530 builtin_ls(FILE *out, const char *file)
531 {
532     int flags = LS_SORT_NAME;
533
534     if(*file == '-') {
535         const char *p;
536         for(p = file + 1; *p; p++) {
537             switch(*p) {
538             case 'a':
539             case 'A':
540                 flags &= ~LS_IGNORE_DOT;
541                 break;
542             case 'C':
543                 break;
544             case 'd':
545                 flags |= LS_DIRS;
546                 break;
547             case 'f':
548                 flags = (flags & ~LS_SORT_MODE);
549                 break;
550             case 'i':
551                 flags |= flags | LS_INODE;
552                 break;
553             case 'l':
554                 break;
555             case 't':
556                 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME;
557                 break;
558             case 's':
559                 flags |= LS_SIZE;
560                 break;
561             case 'S':
562                 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE;
563                 break;
564             case 'r':
565                 flags |= LS_SORT_REVERSE;
566                 break;
567             }
568         }
569         file = ".";
570     }
571     list_files(out, &file, 1, flags);
572     sec_fflush(out);
573 }