]> arthur.barton.de Git - netatalk.git/blob - bin/ad/ad_ls.c
8cda318a99d2e4afe04ffa4392e981d10d542909
[netatalk.git] / bin / ad / ad_ls.c
1 /* 
2    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8  
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <time.h>
33
34 #include <atalk/adouble.h>
35 #include <atalk/cnid.h>
36 #include "ad.h"
37
38 #define ADv2_DIRNAME ".AppleDouble"
39
40 #define DIR_DOT_OR_DOTDOT(a) \
41         ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
42
43 static volatile sig_atomic_t alarmed;
44
45 /* ls options */
46 static int ls_a;
47 static int ls_l;
48 static int ls_R;
49 static int ls_d;
50 static int ls_u;
51
52 /* Used for pretty printing */
53 static int first = 1;
54 static int recursion;
55
56 static char           *netatalk_dirs[] = {
57     ADv2_DIRNAME,
58     ".AppleDB",
59     ".AppleDesktop",
60     NULL
61 };
62
63 static char *labels[] = {
64     "---",
65     "gry",
66     "gre",
67     "vio",
68     "blu",
69     "yel",
70     "red",
71     "ora"
72 };
73
74 /*
75   SIGNAL handling:
76   catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
77 */
78
79 static void sig_handler(int signo)
80 {
81     alarmed = 1;
82     return;
83 }
84
85 static void set_signal(void)
86 {
87     struct sigaction sv;
88
89     sv.sa_handler = sig_handler;
90     sv.sa_flags = SA_RESTART;
91     sigemptyset(&sv.sa_mask);
92     if (sigaction(SIGTERM, &sv, NULL) < 0)
93         ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
94
95     if (sigaction(SIGINT, &sv, NULL) < 0)
96         ERROR("error in sigaction(SIGINT): %s", strerror(errno));
97
98     memset(&sv, 0, sizeof(struct sigaction));
99     sv.sa_handler = SIG_IGN;
100     sigemptyset(&sv.sa_mask);
101
102     if (sigaction(SIGABRT, &sv, NULL) < 0)
103         ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
104
105     if (sigaction(SIGHUP, &sv, NULL) < 0)
106         ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
107
108     if (sigaction(SIGQUIT, &sv, NULL) < 0)
109         ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
110 }
111
112 /*
113   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
114   Returns pointer to name or NULL.
115 */
116 static const char *check_netatalk_dirs(const char *name)
117 {
118     int c;
119
120     for (c=0; netatalk_dirs[c]; c++) {
121         if ((strcmp(name, netatalk_dirs[c])) == 0)
122             return netatalk_dirs[c];
123     }
124     return NULL;
125 }
126
127
128 static void usage_ls(void)
129 {
130     printf(
131         "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n"
132         "  -l Long Output [-u: unix info]:\n"
133         "     <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n"
134         "     FinderFlags (valid for (f)ile and/or (d)irectory):\n"
135         "       d = On Desktop (f/d)\n"
136         "       e = Hidden extension (f/d)\n"
137         "       m = Shared (can run multiple times) (f)\n"
138         "       n = No INIT resources (f)\n"
139         "       i = Inited (f/d)\n"
140         "       c = Custom icon (f/d)\n"
141         "       t = Stationery (f)\n"
142         "       s = Name locked (f/d)\n"
143         "       b = Bundle (f/d)\n"
144         "       v = Invisible (f/d)\n"
145         "       a = Alias file (f/d)\n\n"
146         "     AFPAttributes:\n"
147         "       y = System (f/d)\n"
148         "       w = No write (f)\n"
149         "       p = Needs backup (f/d)\n"
150         "       r = No rename (f/d)\n"
151         "       l = No delete (f/d)\n"
152         "       o = No copy (f)\n\n"
153         "     Note: any letter appearing in uppercase means the flag is set\n"
154         "           but it's a directory for which the flag is not allowed.\n"
155         );
156 }
157
158 static void print_numlinks(const struct stat *statp)
159 {
160     printf("%5ld", (long)statp->st_nlink);
161 }
162
163 static void print_owner(const struct stat *statp)
164 {
165     struct passwd *pwd = getpwuid(statp->st_uid);
166
167     if (pwd == NULL)
168         printf(" %-8ld", (long)statp->st_uid);
169     else
170         printf(" %-8s", pwd->pw_name);
171 }
172
173 static void print_group(const struct stat *statp)
174 {
175     struct group *grp = getgrgid(statp->st_gid);
176
177     if (grp == NULL)
178         printf(" %-8ld", (long)statp->st_gid);
179     else
180         printf(" %-8s", grp->gr_name);
181 }
182
183 static void print_size(const struct stat *statp)
184 {
185     switch (statp->st_mode & S_IFMT) {
186     case S_IFCHR:
187     case S_IFBLK:
188         printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
189                (unsigned)(statp->st_rdev & 0xFF));
190         break;
191     default:
192         printf("%9lu", (unsigned long)statp->st_size);
193     }
194 }
195
196 static void print_date(const struct stat *statp)
197 {
198     time_t now;
199     double diff;
200     char buf[100], *fmt;
201
202     if (time(&now) == -1) {
203         printf(" ????????????");
204         return;
205     }
206     diff = difftime(now, statp->st_mtime);
207     if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
208         fmt = "%b %e  %Y";
209     else
210         fmt = "%b %e %H:%M";
211     strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
212     printf(" %s", buf);
213 }
214
215 static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
216 {
217     int adflags = 0;
218     struct adouble ad;
219     char *FinderInfo;
220     uint16_t FinderFlags;
221     uint16_t AFPattributes;
222     char type[5] = "----";
223     char creator[5] = "----";
224     int i;
225     uint32_t cnid;
226
227     if (S_ISDIR(st->st_mode))
228         adflags = ADFLAGS_DIR;
229
230     if (vol->vol->v_path == NULL)
231         return;
232
233     ad_init(&ad, vol->vol);
234
235     if ( ad_metadata(path, adflags, &ad) < 0 )
236         return;
237
238     FinderInfo = ad_entry(&ad, ADEID_FINDERI);
239
240     memcpy(&FinderFlags, FinderInfo + 8, 2);
241     FinderFlags = ntohs(FinderFlags);
242
243     memcpy(type, FinderInfo, 4);
244     memcpy(creator, FinderInfo + 4, 4);
245
246     ad_getattr(&ad, &AFPattributes);
247     AFPattributes = ntohs(AFPattributes);
248
249     /*
250       Finder flags. Lowercase means valid, uppercase means invalid because
251       object is a dir and flag is only valid for files.
252     */
253     putchar(' ');
254     if (FinderFlags & FINDERINFO_ISONDESK)
255         putchar('d');
256     else
257         putchar('-');
258
259     if (FinderFlags & FINDERINFO_HIDEEXT)
260         putchar('e');
261     else
262         putchar('-');
263
264     if (FinderFlags & FINDERINFO_ISHARED) {
265         if (adflags & ADFLAGS_DIR)
266             putchar('M');
267         else
268             putchar('m');
269     } else
270         putchar('-');
271
272     if (FinderFlags & FINDERINFO_HASNOINITS) {
273         if (adflags & ADFLAGS_DIR)
274             putchar('N');
275         else
276             putchar('n');
277     } else
278         putchar('-');
279
280     if (FinderFlags & FINDERINFO_HASBEENINITED)
281         putchar('i');
282     else
283         putchar('-');
284
285     if (FinderFlags & FINDERINFO_HASCUSTOMICON)
286         putchar('c');
287     else
288         putchar('-');
289
290     if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
291         if (adflags & ADFLAGS_DIR)
292             putchar('T');
293         else
294             putchar('t');
295     } else
296         putchar('-');
297
298     if (FinderFlags & FINDERINFO_NAMELOCKED)
299         putchar('s');
300     else
301         putchar('-');
302
303     if (FinderFlags & FINDERINFO_HASBUNDLE)
304         putchar('b');
305     else
306         putchar('-');
307
308     if (FinderFlags & FINDERINFO_INVISIBLE)
309         putchar('v');
310     else
311         putchar('-');
312
313     if (FinderFlags & FINDERINFO_ISALIAS)
314         putchar('a');
315     else
316         putchar('-');
317
318     putchar(' ');
319
320     /* AFP attributes */
321     if (AFPattributes & ATTRBIT_SYSTEM)
322         putchar('y');
323     else
324         putchar('-');
325
326     if (AFPattributes & ATTRBIT_NOWRITE) {
327         if (adflags & ADFLAGS_DIR)
328             putchar('W');
329         else
330             putchar('w');
331     } else
332         putchar('-');
333
334     if (AFPattributes & ATTRBIT_BACKUP)
335         putchar('p');
336     else
337         putchar('-');
338
339     if (AFPattributes & ATTRBIT_NORENAME)
340         putchar('r');
341     else
342         putchar('-');
343
344     if (AFPattributes & ATTRBIT_NODELETE)
345         putchar('l');
346     else
347         putchar('-');
348
349     if (AFPattributes & ATTRBIT_NOCOPY) {
350         if (adflags & ADFLAGS_DIR)
351             putchar('O');
352         else
353             putchar('o');                
354     } else
355         putchar('-');
356
357     /* Color */
358     printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
359
360     /* Type & Creator */
361     for(i=0; i<4; i++) {
362         if (isalnum(type[i]))
363             putchar(type[i]);
364         else
365             putchar('-');
366     }
367     putchar(' '); 
368     for(i=0; i<4; i++) {
369         if (isalnum(creator[i]))
370             putchar(creator[i]);
371         else
372             putchar('-');
373     }
374     putchar(' '); 
375
376     /* CNID */
377     cnid = ad_forcegetid(&ad);
378     if (cnid)
379         printf(" %10u ", ntohl(cnid));
380     else
381         printf(" !ADVOL_CACHE ");
382
383     ad_close(&ad, ADFLAGS_HF);
384 }
385
386 #define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
387 #define MODE(b) ((st->st_mode & (b)) == (b))
388
389 static void print_mode(const struct stat *st)
390 {
391     if (TYPE(S_IFBLK))
392         putchar('b');
393     else if (TYPE(S_IFCHR))
394         putchar('c');
395     else if (TYPE(S_IFDIR))
396         putchar('d');
397     else if (TYPE(S_IFIFO))
398         putchar('p');
399     else if (TYPE(S_IFREG))
400         putchar('-');
401     else if (TYPE(S_IFLNK))
402         putchar('l');
403     else if (TYPE(S_IFSOCK))
404         putchar('s');
405     else
406         putchar('?');
407     putchar(MODE(S_IRUSR) ? 'r' : '-');
408     putchar(MODE(S_IWUSR) ? 'w' : '-');
409     if (MODE(S_ISUID)) {
410         if (MODE(S_IXUSR))
411             putchar('s');
412         else
413             putchar('S');
414     }
415     else if (MODE(S_IXUSR))
416         putchar('x');
417     else
418         putchar('-');
419     putchar(MODE(S_IRGRP) ? 'r' : '-');
420     putchar(MODE(S_IWGRP) ? 'w' : '-');
421     if (MODE(S_ISGID)) {
422         if (MODE(S_IXGRP))
423             putchar('s');
424         else
425             putchar('S');
426     }
427     else if (MODE(S_IXGRP))
428         putchar('x');
429     else
430         putchar('-');
431     putchar(MODE(S_IROTH) ? 'r' : '-');
432     putchar(MODE(S_IWOTH) ? 'w' : '-');
433     if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
434         if (MODE(S_IXOTH))
435             putchar('t');
436         else
437             putchar('T');
438     }
439     else if (MODE(S_IXOTH))
440         putchar('x');
441     else
442         putchar('-');
443 }
444 #undef TYPE
445 #undef MODE
446
447 static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
448 {
449     if ( ! ls_l) {
450         printf("%s  ", path);
451         if (ls_d)
452             printf("\n");
453         return 0;
454     }
455
456     /* Long output */
457     if (ls_u) {
458         print_mode(st);
459         print_numlinks(st);
460         print_owner(st);
461         print_group(st);
462         print_size(st);
463         print_date(st);
464     }
465     print_flags(path, vol, st);
466     printf("  %s\n", path);    
467
468
469     return 0;
470 }
471
472 static int ad_ls_r(char *path, afpvol_t *vol)
473 {
474     int ret = 0, cwd, dirprinted = 0, dirempty;
475     const char *name;
476     char *tmp;
477     static char cwdpath[MAXPATHLEN+1];
478     DIR *dp;
479     struct dirent *ep;
480     static struct stat st;      /* Save some stack space */
481
482     if ( first)
483         cwdpath[0] = 0;
484     else
485         strcat(cwdpath, "/");
486
487     strcat(cwdpath, path);
488     first = 0;
489
490     if (lstat(path, &st) < 0) {
491         perror("Can't stat");
492         return -1;
493     }
494     /* If its a file or a dir with -d option call ad_print and return */
495     if (S_ISREG(st.st_mode) || ls_d)
496         return ad_print(path, &st, vol);
497
498     /* Its a dir: chdir to it remembering where we started */
499     if ((cwd = open(".", O_RDONLY)) == -1) {
500         perror("Cant open .");
501         return -1;
502     }
503     if (chdir(path) != 0) {
504         perror("Cant chdir");
505         close(cwd);
506         return -1;
507     }
508
509     if ((dp = opendir (".")) == NULL) {
510         perror("Couldn't opendir .");
511         return -1;
512     }
513
514     /* First run: print everything */
515     dirempty = 1;
516     while ((ep = readdir (dp))) {
517         if (alarmed) {
518             ret = -1;
519             goto exit;
520         }
521
522         /* Check if its "." or ".." */
523         if (DIR_DOT_OR_DOTDOT(ep->d_name))
524             continue;
525
526         /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
527         if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
528             continue;
529
530         if ((ep->d_name[0] == '.') && ! ls_a)
531             continue;
532
533         dirempty = 0;
534
535         if (recursion && ! dirprinted) {
536             printf("\n%s:\n", cwdpath);
537             dirprinted = 1;
538         }
539
540         if (lstat(ep->d_name, &st) < 0) {
541             perror("Can't stat");
542             return -1;
543         }
544
545         ret = ad_print(ep->d_name, &st, vol);
546         if (ret != 0)
547             goto exit;
548     }
549
550     if (! ls_l && ! dirempty)
551         printf("\n");
552
553     /* Second run: recurse to dirs */
554     if (ls_R) {
555         rewinddir(dp);
556         while ((ep = readdir (dp))) {
557             if (alarmed) {
558                 ret = -1;
559                 goto exit;
560             }
561
562             /* Check if its "." or ".." */
563             if (DIR_DOT_OR_DOTDOT(ep->d_name))
564                 continue;
565
566             /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
567             if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
568                 continue;
569
570             if ((ret = lstat(ep->d_name, &st)) < 0) {
571                 perror("Can't stat");
572                 goto exit;
573             }
574
575             /* Recursion */
576             if (S_ISDIR(st.st_mode)) {
577                 recursion = 1;
578                 ret = ad_ls_r(ep->d_name, vol);
579             }
580             if (ret != 0)
581                 goto exit;
582         }
583     }
584
585 exit:
586     closedir(dp);
587     fchdir(cwd);
588     close(cwd);
589
590     tmp = strrchr(cwdpath, '/');
591     if (tmp)
592         *tmp = 0;
593
594     return ret;
595 }
596
597 int ad_ls(int argc, char **argv)
598 {
599     int c, firstarg;
600     afpvol_t vol;
601     struct stat st;
602
603     while ((c = getopt(argc, argv, ":adlRu")) != -1) {
604         switch(c) {
605         case 'a':
606             ls_a = 1;
607             break;
608         case 'd':
609             ls_d = 1;
610             break;
611         case 'l':
612             ls_l = 1;
613             break;
614         case 'R':
615             ls_R = 1;
616             break;
617         case 'u':
618             ls_u = 1;
619             break;
620         case ':':
621         case '?':
622             usage_ls();
623             return -1;
624             break;
625         }
626
627     }
628
629     set_signal();
630     cnid_init();
631
632     if ((argc - optind) == 0) {
633         openvol(".", &vol);
634         ad_ls_r(".", &vol);
635         closevol(&vol);
636     }
637     else {
638         int havefile = 0;
639
640         firstarg = optind;
641
642         /* First run: only print files from argv paths */
643         while(optind < argc) {
644             if (stat(argv[optind], &st) != 0)
645                 goto next;
646             if (S_ISDIR(st.st_mode))
647                 goto next;
648
649             havefile = 1;
650             first = 1;
651             recursion = 0;
652
653             openvol(argv[optind], &vol);
654             ad_ls_r(argv[optind], &vol);
655             closevol(&vol);
656         next:
657             optind++;
658         }
659         if (havefile && (! ls_l))
660             printf("\n");
661
662         /* Second run: print dirs */
663         optind = firstarg;
664         while(optind < argc) {
665             if (stat(argv[optind], &st) != 0)
666                 goto next2;
667             if ( ! S_ISDIR(st.st_mode))
668                 goto next2;
669             if ((optind > firstarg) || havefile)
670                 printf("\n%s:\n", argv[optind]);
671
672             first = 1;
673             recursion = 0;
674
675             openvol(argv[optind], &vol);
676             ad_ls_r(argv[optind], &vol);
677             closevol(&vol);
678
679         next2:
680             optind++;
681         }
682
683
684     }
685
686     return 0;
687 }