2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
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.
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.
17 #endif /* HAVE_CONFIG_H */
20 #include <sys/types.h>
34 #include <atalk/adouble.h>
35 #include <atalk/cnid.h>
36 #include <atalk/volinfo.h>
39 #define ADv2_DIRNAME ".AppleDouble"
41 #define DIR_DOT_OR_DOTDOT(a) \
42 ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
44 static volatile sig_atomic_t alarmed;
53 /* Used for pretty printing */
57 static char *netatalk_dirs[] = {
64 static char *labels[] = {
77 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
80 static void sig_handler(int signo)
86 static void set_signal(void)
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));
96 if (sigaction(SIGINT, &sv, NULL) < 0)
97 ERROR("error in sigaction(SIGINT): %s", strerror(errno));
99 memset(&sv, 0, sizeof(struct sigaction));
100 sv.sa_handler = SIG_IGN;
101 sigemptyset(&sv.sa_mask);
103 if (sigaction(SIGABRT, &sv, NULL) < 0)
104 ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
106 if (sigaction(SIGHUP, &sv, NULL) < 0)
107 ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
109 if (sigaction(SIGQUIT, &sv, NULL) < 0)
110 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
114 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
115 Returns pointer to name or NULL.
117 static const char *check_netatalk_dirs(const char *name)
121 for (c=0; netatalk_dirs[c]; c++) {
122 if ((strcmp(name, netatalk_dirs[c])) == 0)
123 return netatalk_dirs[c];
129 static void usage_ls(void)
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"
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"
159 static void print_numlinks(const struct stat *statp)
161 printf("%5ld", (long)statp->st_nlink);
164 static void print_owner(const struct stat *statp)
166 struct passwd *pwd = getpwuid(statp->st_uid);
169 printf(" %-8ld", (long)statp->st_uid);
171 printf(" %-8s", pwd->pw_name);
174 static void print_group(const struct stat *statp)
176 struct group *grp = getgrgid(statp->st_gid);
179 printf(" %-8ld", (long)statp->st_gid);
181 printf(" %-8s", grp->gr_name);
184 static void print_size(const struct stat *statp)
186 switch (statp->st_mode & S_IFMT) {
189 printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
190 (unsigned)(statp->st_rdev & 0xFF));
193 printf("%9lu", (unsigned long)statp->st_size);
197 static void print_date(const struct stat *statp)
203 if (time(&now) == -1) {
204 printf(" ????????????");
207 diff = difftime(now, statp->st_mtime);
208 if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
212 strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
216 static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
221 uint16_t FinderFlags;
222 uint16_t AFPattributes;
223 char type[5] = "----";
224 char creator[5] = "----";
228 if (S_ISDIR(st->st_mode))
229 adflags = ADFLAGS_DIR;
231 if (vol->volinfo.v_path == NULL)
234 ad_init(&ad, vol->volinfo.v_adouble, vol->volinfo.v_ad_options);
236 if ( ad_metadata(path, adflags, &ad) < 0 )
239 FinderInfo = ad_entry(&ad, ADEID_FINDERI);
241 memcpy(&FinderFlags, FinderInfo + 8, 2);
242 FinderFlags = ntohs(FinderFlags);
244 memcpy(type, FinderInfo, 4);
245 memcpy(creator, FinderInfo + 4, 4);
247 ad_getattr(&ad, &AFPattributes);
248 AFPattributes = ntohs(AFPattributes);
251 Finder flags. Lowercase means valid, uppercase means invalid because
252 object is a dir and flag is only valid for files.
255 if (FinderFlags & FINDERINFO_ISONDESK)
260 if (FinderFlags & FINDERINFO_HIDEEXT)
265 if (FinderFlags & FINDERINFO_ISHARED) {
266 if (adflags & ADFLAGS_DIR)
273 if (FinderFlags & FINDERINFO_HASNOINITS) {
274 if (adflags & ADFLAGS_DIR)
281 if (FinderFlags & FINDERINFO_HASBEENINITED)
286 if (FinderFlags & FINDERINFO_HASCUSTOMICON)
291 if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
292 if (adflags & ADFLAGS_DIR)
299 if (FinderFlags & FINDERINFO_NAMELOCKED)
304 if (FinderFlags & FINDERINFO_HASBUNDLE)
309 if (FinderFlags & FINDERINFO_INVISIBLE)
314 if (FinderFlags & FINDERINFO_ISALIAS)
322 if (AFPattributes & ATTRBIT_SYSTEM)
327 if (AFPattributes & ATTRBIT_NOWRITE) {
328 if (adflags & ADFLAGS_DIR)
335 if (AFPattributes & ATTRBIT_BACKUP)
340 if (AFPattributes & ATTRBIT_NORENAME)
345 if (AFPattributes & ATTRBIT_NODELETE)
350 if (AFPattributes & ATTRBIT_NOCOPY) {
351 if (adflags & ADFLAGS_DIR)
359 printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
363 if (isalnum(type[i]))
370 if (isalnum(creator[i]))
378 cnid = ad_forcegetid(&ad);
380 printf(" %10u ", ntohl(cnid));
382 printf(" !ADVOL_CACHE ");
384 ad_close_metadata(&ad);
387 #define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
388 #define MODE(b) ((st->st_mode & (b)) == (b))
390 static void print_mode(const struct stat *st)
394 else if (TYPE(S_IFCHR))
396 else if (TYPE(S_IFDIR))
398 else if (TYPE(S_IFIFO))
400 else if (TYPE(S_IFREG))
402 else if (TYPE(S_IFLNK))
404 else if (TYPE(S_IFSOCK))
408 putchar(MODE(S_IRUSR) ? 'r' : '-');
409 putchar(MODE(S_IWUSR) ? 'w' : '-');
416 else if (MODE(S_IXUSR))
420 putchar(MODE(S_IRGRP) ? 'r' : '-');
421 putchar(MODE(S_IWGRP) ? 'w' : '-');
428 else if (MODE(S_IXGRP))
432 putchar(MODE(S_IROTH) ? 'r' : '-');
433 putchar(MODE(S_IWOTH) ? 'w' : '-');
434 if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
440 else if (MODE(S_IXOTH))
448 static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
466 print_flags(path, vol, st);
467 printf(" %s\n", path);
473 static int ad_ls_r(char *path, afpvol_t *vol)
475 int ret = 0, cwd, dirprinted = 0, dirempty;
478 static char cwdpath[MAXPATHLEN+1];
481 static struct stat st; /* Save some stack space */
486 strcat(cwdpath, "/");
488 strcat(cwdpath, path);
491 if (lstat(path, &st) < 0) {
492 perror("Can't stat");
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);
499 /* Its a dir: chdir to it remembering where we started */
500 if ((cwd = open(".", O_RDONLY)) == -1) {
501 perror("Cant open .");
504 if (chdir(path) != 0) {
505 perror("Cant chdir");
510 if ((dp = opendir (".")) == NULL) {
511 perror("Couldn't opendir .");
515 /* First run: print everything */
517 while ((ep = readdir (dp))) {
523 /* Check if its "." or ".." */
524 if (DIR_DOT_OR_DOTDOT(ep->d_name))
527 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
528 if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
531 if ((ep->d_name[0] == '.') && ! ls_a)
536 if (recursion && ! dirprinted) {
537 printf("\n%s:\n", cwdpath);
541 if (lstat(ep->d_name, &st) < 0) {
542 perror("Can't stat");
546 ret = ad_print(ep->d_name, &st, vol);
551 if (! ls_l && ! dirempty)
554 /* Second run: recurse to dirs */
557 while ((ep = readdir (dp))) {
563 /* Check if its "." or ".." */
564 if (DIR_DOT_OR_DOTDOT(ep->d_name))
567 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
568 if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
571 if ((ret = lstat(ep->d_name, &st)) < 0) {
572 perror("Can't stat");
577 if (S_ISDIR(st.st_mode)) {
579 ret = ad_ls_r(ep->d_name, vol);
591 tmp = strrchr(cwdpath, '/');
598 int ad_ls(int argc, char **argv)
604 while ((c = getopt(argc, argv, ":adlRu")) != -1) {
633 if ((argc - optind) == 0) {
643 /* First run: only print files from argv paths */
644 while(optind < argc) {
645 if (stat(argv[optind], &st) != 0)
647 if (S_ISDIR(st.st_mode))
654 openvol(argv[optind], &vol);
655 ad_ls_r(argv[optind], &vol);
660 if (havefile && (! ls_l))
663 /* Second run: print dirs */
665 while(optind < argc) {
666 if (stat(argv[optind], &st) != 0)
668 if ( ! S_ISDIR(st.st_mode))
670 if ((optind > firstarg) || havefile)
671 printf("\n%s:\n", argv[optind]);
676 openvol(argv[optind], &vol);
677 ad_ls_r(argv[optind], &vol);