3 (Linux Extended Attributes)
5 Copyright (C) 2001-2002 Andreas Gruenbacher <a.gruenbacher@bestbits.at>
6 Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved.
8 This program is free software: you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #include <attr/xattr.h>
33 #include "walk_tree.h"
36 #define CMD_LINE_OPTIONS "n:de:m:hRLP"
37 #define CMD_LINE_SPEC "[-hRLP] [-n name|-d] [-e en] [-m pattern] path..."
39 struct option long_options[] = {
40 { "name", 1, 0, 'n' },
41 { "dump", 0, 0, 'd' },
42 { "encoding", 1, 0, 'e' },
43 { "match", 1, 0, 'm' },
44 { "only-values", 0, 0, 'v' },
45 { "no-dereference", 0, 0, 'h' },
46 { "absolute-names", 0, 0, 'a' },
47 { "recursive", 0, 0, 'R' },
48 { "logical", 0, 0, 'L' },
49 { "physical", 0, 0, 'P' },
50 { "version", 0, 0, 'V' },
51 { "help", 0, 0, 'H' },
55 int walk_flags = WALK_TREE_DEREFERENCE;
56 int opt_dump; /* dump attribute values (or only list the names) */
57 char *opt_name; /* dump named attributes */
58 char *opt_name_pattern = "^user\\."; /* include only matching names */
59 char *opt_encoding; /* encode values automatically (NULL), or as "text",
61 char opt_value_only; /* dump the value only, without any decoration */
62 int opt_strip_leading_slash = 1; /* strip leading '/' from path names */
70 static const char *xquote(const char *str, const char *quote_chars)
72 const char *q = quote(str, quote_chars);
74 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
80 int do_getxattr(const char *path, const char *name, void *value, size_t size)
82 return ((walk_flags & WALK_TREE_DEREFERENCE) ?
83 getxattr : lgetxattr)(path, name, value, size);
86 int do_listxattr(const char *path, char *list, size_t size)
88 return ((walk_flags & WALK_TREE_DEREFERENCE) ?
89 listxattr : llistxattr)(path, list, size);
92 const char *strerror_ea(int err)
95 /* The Linux kernel does not define ENOATTR, but maps it to ENODATA. */
97 return _("No such attribute");
102 int pstrcmp(const void *a, const void *b)
104 return strcmp(*(const char **)a, *(const char **)b);
107 int well_enough_printable(const char *value, size_t size)
111 for (n=0; n < size; n++)
112 if (!isprint(*value++))
115 return (size >= nonpr*8); /* no more than 1/8 non-printable chars */
118 const char *encode(const char *value, size_t *size)
120 static char *encoded;
121 static size_t encoded_size;
124 if (opt_encoding == NULL) {
125 if (well_enough_printable(value, *size))
132 if (strcmp(enc, "text") == 0) {
135 for (e=(char *)value; e < value + *size; e++) {
136 if (*e == '\0' || *e == '\n' || *e == '\r')
138 else if (*e == '\\' || *e == '"')
141 if (high_water_alloc((void **)&encoded, &encoded_size,
142 *size + extra + 3)) {
149 for (n = 0; n < *size; n++, value++) {
150 if (*value == '\0' && n + 1 == *size)
152 if (*value == '\0' || *value == '\n' || *value == '\r') {
154 *e++ = '0' + ((unsigned char)*value >> 6);
155 *e++ = '0' + (((unsigned char)*value & 070) >> 3);
156 *e++ = '0' + ((unsigned char)*value & 07);
157 } else if (*value == '\\' || *value == '"') {
166 *size = (e - encoded);
167 } else if (strcmp(enc, "hex") == 0) {
168 static const char *digits = "0123456789abcdef";
171 if (high_water_alloc((void **)&encoded, &encoded_size,
178 *e++='0'; *e++ = 'x';
179 for (n = 0; n < *size; n++, value++) {
180 *e++ = digits[((unsigned char)*value >> 4)];
181 *e++ = digits[((unsigned char)*value & 0x0F)];
184 *size = (e - encoded);
185 } else if (strcmp(enc, "base64") == 0) {
186 static const char *digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
187 "ghijklmnopqrstuvwxyz0123456789+/";
190 if (high_water_alloc((void **)&encoded, &encoded_size,
191 (*size + 2) / 3 * 4 + 1)) {
197 *e++='0'; *e++ = 's';
198 for (n=0; n + 2 < *size; n += 3) {
199 *e++ = digits[(unsigned char)value[0] >> 2];
200 *e++ = digits[(((unsigned char)value[0] & 0x03) << 4) |
201 (((unsigned char)value[1] & 0xF0) >> 4)];
202 *e++ = digits[(((unsigned char)value[1] & 0x0F) << 2) |
203 ((unsigned char)value[2] >> 6)];
204 *e++ = digits[(unsigned char)value[2] & 0x3F];
207 if (*size - n == 2) {
208 *e++ = digits[(unsigned char)value[0] >> 2];
209 *e++ = digits[(((unsigned char)value[0] & 0x03) << 4) |
210 (((unsigned char)value[1] & 0xF0) >> 4)];
211 *e++ = digits[((unsigned char)value[1] & 0x0F) << 2];
213 } else if (*size - n == 1) {
214 *e++ = digits[(unsigned char)value[0] >> 2];
215 *e++ = digits[((unsigned char)value[0] & 0x03) << 4];
220 *size = (e - encoded);
225 int print_attribute(const char *path, const char *name, int *header_printed)
228 static size_t value_size;
232 if (opt_dump || opt_value_only) {
233 rval = do_getxattr(path, name, NULL, 0);
235 fprintf(stderr, "%s: ", xquote(path, "\n\r"));
236 fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"),
240 if (high_water_alloc((void **)&value, &value_size, rval)) {
245 rval = do_getxattr(path, name, value, value_size);
247 fprintf(stderr, "%s: ", xquote(path, "\n\r"));
248 fprintf(stderr, "%s: %s\n", xquote(name, "\n\r"),
255 if (opt_strip_leading_slash) {
257 if (!absolute_warning) {
258 fprintf(stderr, _("%s: Removing leading '/' "
259 "from absolute path names\n"),
261 absolute_warning = 1;
265 } else if (*path == '.' && *(path+1) == '/')
266 while (*++path == '/')
272 if (!*header_printed && !opt_value_only) {
273 printf("# file: %s\n", xquote(path, "\n\r"));
278 fwrite(value, length, 1, stdout);
280 const char *enc = encode(value, &length);
283 printf("%s=%s\n", xquote(name, "=\n\r"), enc);
285 puts(xquote(name, "=\n\r"));
290 int list_attributes(const char *path, int *header_printed)
293 static size_t list_size;
295 static size_t names_size;
300 length = do_listxattr(path, NULL, 0);
302 fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"),
306 } else if (length == 0)
309 if (high_water_alloc((void **)&list, &list_size, length)) {
315 length = do_listxattr(path, list, list_size);
317 perror(xquote(path, "\n\r"));
322 for (l = list; l != list + length; l = strchr(l, '\0')+1) {
323 if (*l == '\0') /* not a name, kernel bug */
326 if (regexec(&name_regex, l, 0, NULL, 0) != 0)
329 if (names_size < (num_names+1) * sizeof(*names)) {
330 if (high_water_alloc((void **)&names, &names_size,
331 (num_names+1) * sizeof(*names))) {
338 names[num_names++] = l;
341 qsort(names, num_names, sizeof(*names), pstrcmp);
346 for (n = 0; n < num_names; n++)
347 print_attribute(path, names[n], header_printed);
352 int do_print(const char *path, const struct stat *stat, int walk_flags,
355 int header_printed = 0;
357 if (walk_flags & WALK_TREE_FAILED) {
358 fprintf(stderr, "%s: %s: %s\n", progname, xquote(path, "\n\r"),
364 print_attribute(path, opt_name, &header_printed);
366 list_attributes(path, &header_printed);
375 printf(_("%s %s -- get extended attributes\n"),
377 printf(_("Usage: %s %s\n"),
378 progname, _(CMD_LINE_SPEC));
380 " -n, --name=name get the named extended attribute value\n"
381 " -d, --dump get all extended attribute values\n"
382 " -e, --encoding=... encode values (as 'text', 'hex' or 'base64')\n"
383 " --match=pattern only get attributes with names matching pattern\n"
384 " --only-values print the bare values only\n"
385 " -h, --no-dereference do not dereference symbolic links\n"
386 " --absolute-names don't strip leading '/' in pathnames\n"
387 " -R, --recursive recurse into subdirectories\n"
388 " -L, --logical logical walk, follow symbolic links\n"
389 " -P --physical physical walk, do not follow symbolic links\n"
390 " --version print version and exit\n"
391 " --help this help text\n"));
394 int main(int argc, char *argv[])
398 progname = basename(argv[0]);
400 setlocale(LC_CTYPE, "");
401 setlocale(LC_MESSAGES, "");
402 bindtextdomain(PACKAGE, LOCALEDIR);
405 while ((opt = getopt_long(argc, argv, CMD_LINE_OPTIONS,
406 long_options, NULL)) != -1) {
408 case 'a': /* absolute names */
409 opt_strip_leading_slash = 0;
412 case 'd': /* dump attribute values */
416 case 'e': /* encoding */
417 if (strcmp(optarg, "text") != 0 &&
418 strcmp(optarg, "hex") != 0 &&
419 strcmp(optarg, "base64") != 0)
421 opt_encoding = optarg;
428 case 'h': /* do not dereference symlinks */
429 walk_flags &= ~WALK_TREE_DEREFERENCE;
432 case 'n': /* get named attribute */
437 case 'm': /* regular expression for filtering names */
438 opt_name_pattern = optarg;
439 if (strcmp(opt_name_pattern, "-") == 0)
440 opt_name_pattern = "";
443 case 'v': /* get attribute values only */
448 walk_flags |= WALK_TREE_LOGICAL;
449 walk_flags &= ~WALK_TREE_PHYSICAL;
453 walk_flags |= WALK_TREE_PHYSICAL;
454 walk_flags &= ~WALK_TREE_LOGICAL;
458 walk_flags |= WALK_TREE_RECURSIVE;
462 printf("%s " VERSION "\n", progname);
465 case ':': /* option missing */
466 case '?': /* unknown option */
474 if (regcomp(&name_regex, opt_name_pattern,
475 REG_EXTENDED | REG_NOSUB) != 0) {
476 fprintf(stderr, _("%s: invalid regular expression \"%s\"\n"),
477 progname, opt_name_pattern);
481 while (optind < argc) {
482 had_errors += walk_tree(argv[optind], walk_flags, 0,
487 return (had_errors ? 1 : 0);
490 fprintf(stderr, _("Usage: %s %s\n"
491 "Try `%s --help' for more information.\n"),
492 progname, CMD_LINE_SPEC, progname);