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