(root)/
acl-2.3.1/
tools/
getfacl.c
       1  /*
       2    File: getfacl.c
       3    (Linux Access Control List Management)
       4  
       5    Copyright (C) 1999-2002
       6    Andreas Gruenbacher, <andreas.gruenbacher@gmail.com>
       7   	
       8    This program is free software; you can redistribute it and/or modify
       9    it under the terms of the GNU General Public License as published by
      10    the Free Software Foundation; either version 2 of the License, or (at
      11    your option) any later version.
      12  
      13    This program is distributed in the hope that it will be useful, but
      14    WITHOUT ANY WARRANTY; without even the implied warranty of
      15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16    General Public License for more details.
      17  
      18    You should have received a copy of the GNU General Public License
      19    along with this library; if not, write to the Free Software
      20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
      21    USA.
      22  */
      23  
      24  #include "config.h"
      25  #include <stdio.h>
      26  #include <errno.h>
      27  #include <sys/acl.h>
      28  #include <acl/libacl.h>
      29  
      30  #include <limits.h>
      31  #include <stdlib.h>
      32  #include <unistd.h>
      33  #include <string.h>
      34  #include <sys/stat.h>
      35  #include <dirent.h>
      36  #include <libgen.h>
      37  #include <getopt.h>
      38  #include "misc.h"
      39  #include "user_group.h"
      40  #include "walk_tree.h"
      41  
      42  #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
      43  
      44  #if !POSIXLY_CORRECT
      45  #  define CMD_LINE_OPTIONS "aceEsRLPtpndvh"
      46  #endif
      47  #define POSIXLY_CMD_LINE_OPTIONS "d"
      48  
      49  struct option long_options[] = {
      50  #if !POSIXLY_CORRECT
      51  	{ "access",	0, 0, 'a' },
      52  	{ "omit-header",	0, 0, 'c' },
      53  	{ "all-effective",	0, 0, 'e' },
      54  	{ "no-effective",	0, 0, 'E' },
      55  	{ "skip-base",	0, 0, 's' },
      56  	{ "recursive",	0, 0, 'R' },
      57  	{ "logical",	0, 0, 'L' },
      58  	{ "physical",	0, 0, 'P' },
      59  	{ "tabular",	0, 0, 't' },
      60  	{ "absolute-names",	0, 0, 'p' },
      61  	{ "numeric",	0, 0, 'n' },
      62  	{ "one-file-system", 0, 0, 1 },
      63  #endif
      64  	{ "default",	0, 0, 'd' },
      65  	{ "version",	0, 0, 'v' },
      66  	{ "help",	0, 0, 'h' },
      67  	{ NULL,		0, 0, 0   }
      68  };
      69  
      70  const char *progname;
      71  const char *cmd_line_options;
      72  
      73  int walk_flags = WALK_TREE_DEREFERENCE_TOPLEVEL;
      74  int opt_print_acl;
      75  int opt_print_default_acl;
      76  int opt_strip_leading_slash = 1;
      77  int opt_comments = 1;  /* include comments */
      78  int opt_skip_base;  /* skip files that only have the base entries */
      79  int opt_tabular;  /* tabular output format (alias `showacl') */
      80  #if POSIXLY_CORRECT
      81  const int posixly_correct = 1;  /* Posix compatible behavior! */
      82  #else
      83  int posixly_correct;  /* Posix compatible behavior? */
      84  #endif
      85  int had_errors;
      86  int absolute_warning;  /* Absolute path warning was issued */
      87  int print_options = TEXT_SOME_EFFECTIVE;
      88  int opt_numeric;  /* don't convert id's to symbolic names */
      89  
      90  
      91  static const char *xquote(const char *str, const char *quote_chars)
      92  {
      93  	const char *q = __acl_quote(str, quote_chars);
      94  	if (q == NULL) {
      95  		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
      96  		exit(1);
      97  	}
      98  	return q;
      99  }
     100  
     101  struct name_list {
     102  	struct name_list *next;
     103  	char name[0];
     104  };
     105  
     106  void free_list(struct name_list *names)
     107  {
     108  	struct name_list *next;
     109  
     110  	while (names) {
     111  		next = names->next;
     112  		free(names);
     113  		names = next;
     114  	}
     115  }
     116  
     117  struct name_list *get_list(const struct stat *st, acl_t acl)
     118  {
     119  	struct name_list *first = NULL, *last = NULL;
     120  	acl_entry_t ent;
     121  	int ret = 0;
     122  
     123  	if (acl != NULL)
     124  		ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &ent);
     125  	if (ret != 1)
     126  		return NULL;
     127  	while (ret > 0) {
     128  		acl_tag_t e_type;
     129  		id_t *id_p;
     130  		const char *name = "";
     131  		int len;
     132  
     133  		acl_get_tag_type(ent, &e_type);
     134  		switch(e_type) {
     135  			case ACL_USER_OBJ:
     136  				name = user_name(st->st_uid, opt_numeric);
     137  				break;
     138  
     139  			case ACL_USER:
     140  				id_p = acl_get_qualifier(ent);
     141  				if (id_p != NULL) {
     142  					name = user_name(*id_p, opt_numeric);
     143  					acl_free(id_p);
     144  				}
     145  				break;
     146  
     147  			case ACL_GROUP_OBJ:
     148  				name = group_name(st->st_gid, opt_numeric);
     149  				break;
     150  
     151  			case ACL_GROUP:
     152  				id_p = acl_get_qualifier(ent);
     153  				if (id_p != NULL) {
     154  					name = group_name(*id_p, opt_numeric);
     155  					acl_free(id_p);
     156  				}
     157  				break;
     158  		}
     159  		name = xquote(name, "\t\n\r");
     160  		len = strlen(name);
     161  		if (last == NULL) {
     162  			first = last = (struct name_list *)
     163  				malloc(sizeof(struct name_list) + len + 1);
     164  		} else {
     165  			last->next = (struct name_list *)
     166  				malloc(sizeof(struct name_list) + len + 1);
     167  			last = last->next;
     168  		}
     169  		if (last == NULL) {
     170  			free_list(first);
     171  			return NULL;
     172  		}
     173  		last->next = NULL;
     174  		strcpy(last->name, name);
     175  
     176  		ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &ent);
     177  	}
     178  	return first;
     179  }
     180  
     181  int max_name_length(struct name_list *names)
     182  {
     183  	int max_len = 0;
     184  	while (names != NULL) {
     185  		struct name_list *next = names->next;
     186  		int len = strlen(names->name);
     187  
     188  		if (len > max_len)
     189  			max_len = len;
     190  		names = next;
     191  	}
     192  	return max_len;
     193  }
     194  
     195  int names_width;
     196  
     197  struct acl_perm_def {
     198  	acl_tag_t	tag;
     199  	char		c;
     200  };
     201  
     202  struct acl_perm_def acl_perm_defs[] = {
     203  	{ ACL_READ,	'r' },
     204  	{ ACL_WRITE,	'w' },
     205  	{ ACL_EXECUTE,	'x' },
     206  	{ 0, 0 }
     207  };
     208  
     209  #define ACL_PERMS (sizeof(acl_perm_defs) / sizeof(struct acl_perm_def) - 1)
     210  
     211  void acl_perm_str(acl_entry_t entry, char *str)
     212  {
     213  	acl_permset_t permset;
     214  	int n;
     215  
     216  	acl_get_permset(entry, &permset);
     217  	for (n = 0; n < (int) ACL_PERMS; n++) {
     218  		str[n] = (acl_get_perm(permset, acl_perm_defs[n].tag) ?
     219  		          acl_perm_defs[n].c : '-');
     220  	}
     221  	str[n] = '\0';
     222  }
     223  
     224  void acl_mask_perm_str(acl_t acl, char *str)
     225  {
     226  	acl_entry_t entry;
     227  
     228  	str[0] = '\0';
     229  	if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) != 1)
     230  		return;
     231  	for(;;) {
     232  		acl_tag_t tag;
     233  
     234  		acl_get_tag_type(entry, &tag);
     235  		if (tag == ACL_MASK) {
     236  			acl_perm_str(entry, str);
     237  			return;
     238  		}
     239  		if (acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) != 1)
     240  			return;
     241  	}
     242  }
     243  
     244  void apply_mask(char *perm, const char *mask)
     245  {
     246  	while (*perm) {
     247  		if (*mask == '-' && *perm >= 'a' && *perm <= 'z')
     248  			*perm = *perm - 'a' + 'A';
     249  		perm++;
     250  		if (*mask)
     251  			mask++;
     252  	}
     253  }
     254  
     255  int show_line(FILE *stream, struct name_list **acl_names,  acl_t acl,
     256                acl_entry_t *acl_ent, const char *acl_mask,
     257                struct name_list **dacl_names, acl_t dacl,
     258  	      acl_entry_t *dacl_ent, const char *dacl_mask)
     259  {
     260  	acl_tag_t tag_type;
     261  	const char *tag, *name;
     262  	char acl_perm[ACL_PERMS+1], dacl_perm[ACL_PERMS+1];
     263  
     264  	if (acl) {
     265  		acl_get_tag_type(*acl_ent, &tag_type);
     266  		name = (*acl_names)->name;
     267  	} else {
     268  		acl_get_tag_type(*dacl_ent, &tag_type);
     269  		name = (*dacl_names)->name;
     270  	}
     271  
     272  	switch(tag_type) {
     273  		case ACL_USER_OBJ:
     274  			tag = "USER";
     275  			break;
     276  		case ACL_USER:
     277  			tag = "user";
     278  			break;
     279  		case ACL_GROUP_OBJ:
     280  			tag = "GROUP";
     281  			break;
     282  		case ACL_GROUP:
     283  			tag = "group";
     284  			break;
     285  		case ACL_MASK:
     286  			tag = "mask";
     287  			break;
     288  		case ACL_OTHER:
     289  			tag = "other";
     290  			break;
     291  		default:
     292  			return -1;
     293  	}
     294  
     295  	memset(acl_perm, ' ', ACL_PERMS);
     296  	acl_perm[ACL_PERMS] = '\0';
     297  	if (acl_ent) {
     298  		acl_perm_str(*acl_ent, acl_perm);
     299  		if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
     300  		    tag_type != ACL_MASK)
     301  			apply_mask(acl_perm, acl_mask);
     302  	}
     303  	memset(dacl_perm, ' ', ACL_PERMS);
     304  	dacl_perm[ACL_PERMS] = '\0';
     305  	if (dacl_ent) {
     306  		acl_perm_str(*dacl_ent, dacl_perm);
     307  		if (tag_type != ACL_USER_OBJ && tag_type != ACL_OTHER &&
     308  		    tag_type != ACL_MASK)
     309  			apply_mask(dacl_perm, dacl_mask);
     310  	}
     311  
     312  	fprintf(stream, "%-5s  %*s  %*s  %*s\n",
     313  	        tag, -names_width, name,
     314  	        -(int)ACL_PERMS, acl_perm,
     315  		-(int)ACL_PERMS, dacl_perm);
     316  
     317  	if (acl_names) {
     318  		acl_get_entry(acl, ACL_NEXT_ENTRY, acl_ent);
     319  		(*acl_names) = (*acl_names)->next;
     320  	}
     321  	if (dacl_names) {
     322  		acl_get_entry(dacl, ACL_NEXT_ENTRY, dacl_ent);
     323  		(*dacl_names) = (*dacl_names)->next;
     324  	}
     325  	return 0;
     326  }
     327  
     328  int do_show(FILE *stream, const char *path_p, const struct stat *st,
     329              acl_t acl, acl_t dacl)
     330  {
     331  	struct name_list *acl_names = get_list(st, acl),
     332  	                 *first_acl_name = acl_names;
     333  	struct name_list *dacl_names = get_list(st, dacl),
     334  	                 *first_dacl_name = dacl_names;
     335  	
     336  	int acl_names_width = max_name_length(acl_names);
     337  	int dacl_names_width = max_name_length(dacl_names);
     338  	acl_entry_t acl_ent;
     339  	acl_entry_t dacl_ent;
     340  	char acl_mask[ACL_PERMS+1], dacl_mask[ACL_PERMS+1];
     341  	int ret;
     342  
     343  	names_width = 8;
     344  	if (acl_names_width > names_width)
     345  		names_width = acl_names_width;
     346  	if (dacl_names_width > names_width)
     347  		names_width = dacl_names_width;
     348  
     349  	acl_mask[0] = '\0';
     350  	if (acl) {
     351  		acl_mask_perm_str(acl, acl_mask);
     352  		ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_ent);
     353  		if (ret == 0)
     354  			acl = NULL;
     355  		if (ret < 0)
     356  			return ret;
     357  	}
     358  	dacl_mask[0] = '\0';
     359  	if (dacl) {
     360  		acl_mask_perm_str(dacl, dacl_mask);
     361  		ret = acl_get_entry(dacl, ACL_FIRST_ENTRY, &dacl_ent);
     362  		if (ret == 0)
     363  			dacl = NULL;
     364  		if (ret < 0)
     365  			return ret;
     366  	}
     367  	fprintf(stream, "# file: %s\n", xquote(path_p, "\n\r"));
     368  	while (acl_names != NULL || dacl_names != NULL) {
     369  		acl_tag_t acl_tag, dacl_tag;
     370  
     371  		if (acl)
     372  			acl_get_tag_type(acl_ent, &acl_tag);
     373  		if (dacl)
     374  			acl_get_tag_type(dacl_ent, &dacl_tag);
     375  
     376  		if (acl && (!dacl || acl_tag < dacl_tag)) {
     377  			show_line(stream, &acl_names, acl, &acl_ent, acl_mask,
     378  			          NULL, NULL, NULL, NULL);
     379  			continue;
     380  		} else if (dacl && (!acl || dacl_tag < acl_tag)) {
     381  			show_line(stream, NULL, NULL, NULL, NULL,
     382  			          &dacl_names, dacl, &dacl_ent, dacl_mask);
     383  			continue;
     384  		} else {
     385  			if (acl_tag == ACL_USER || acl_tag == ACL_GROUP) {
     386  				int id_cmp = 0;
     387  
     388  				if (acl_ent && dacl_ent) {
     389  					id_t *acl_id_p = acl_get_qualifier(acl_ent);
     390  					id_t *dacl_id_p = acl_get_qualifier(dacl_ent);
     391  
     392  					id_cmp = (*acl_id_p > *dacl_id_p) -
     393  						 (*acl_id_p < *dacl_id_p);
     394  					acl_free(acl_id_p);
     395  					acl_free(dacl_id_p);
     396  				}
     397  				
     398  				if (acl && (!dacl || id_cmp < 0)) {
     399  					show_line(stream, &acl_names, acl,
     400  					          &acl_ent, acl_mask,
     401  						  NULL, NULL, NULL, NULL);
     402  					continue;
     403  				} else if (dacl && (!acl || id_cmp > 0)) {
     404  					show_line(stream, NULL, NULL, NULL,
     405  					          NULL, &dacl_names, dacl,
     406  						  &dacl_ent, dacl_mask);
     407  					continue;
     408  				}
     409  			}
     410  			show_line(stream, &acl_names,  acl,  &acl_ent, acl_mask,
     411  				  &dacl_names, dacl, &dacl_ent, dacl_mask);
     412  		}
     413  	}
     414  
     415  	free_list(first_acl_name);
     416  	free_list(first_dacl_name);
     417  
     418  	return 0;
     419  }
     420  
     421  /*
     422   * Create an ACL from the file permission bits
     423   * of the file PATH_P.
     424   */
     425  static acl_t
     426  acl_get_file_mode(const char *path_p)
     427  {
     428  	struct stat st;
     429  
     430  	if (stat(path_p, &st) != 0)
     431  		return NULL;
     432  	return acl_from_mode(st.st_mode);
     433  }
     434  
     435  static const char *
     436  flagstr(mode_t mode)
     437  {
     438  	static char str[4];
     439  
     440  	str[0] = (mode & S_ISUID) ? 's' : '-';
     441  	str[1] = (mode & S_ISGID) ? 's' : '-';
     442  	str[2] = (mode & S_ISVTX) ? 't' : '-';
     443  	str[3] = '\0';
     444  	return str;
     445  }
     446  
     447  int do_print(const char *path_p, const struct stat *st, int walk_flags, void *unused)
     448  {
     449  	const char *default_prefix = NULL;
     450  	acl_t acl = NULL, default_acl = NULL;
     451  	int error = 0;
     452  
     453  	if (walk_flags & WALK_TREE_FAILED) {
     454  		fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
     455  			strerror(errno));
     456  		return 1;
     457  	}
     458  
     459  	/*
     460  	 * Symlinks can never have ACLs, so when doing a physical walk, we
     461  	 * skip symlinks altogether, and when doing a half-logical walk, we
     462  	 * skip all non-toplevel symlinks. 
     463  	 */
     464  	if ((walk_flags & WALK_TREE_SYMLINK) &&
     465  	    ((walk_flags & WALK_TREE_PHYSICAL) ||
     466  	     !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL))))
     467  		return 0;
     468  
     469  	if (opt_print_acl) {
     470  		acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
     471  		if (acl == NULL && (errno == ENOSYS || errno == ENOTSUP))
     472  			acl = acl_get_file_mode(path_p);
     473  		if (acl == NULL)
     474  			goto fail;
     475  	}
     476  
     477  	if (opt_print_default_acl && S_ISDIR(st->st_mode)) {
     478  		default_acl = acl_get_file(path_p, ACL_TYPE_DEFAULT);
     479  		if (default_acl == NULL) {
     480  			if (errno != ENOSYS && errno != ENOTSUP)
     481  				goto fail;
     482  		} else if (acl_entries(default_acl) == 0) {
     483  			acl_free(default_acl);
     484  			default_acl = NULL;
     485  		}
     486  	}
     487  
     488  	if (opt_skip_base &&
     489  	    (!acl || acl_equiv_mode(acl, NULL) == 0) && !default_acl)
     490  		goto cleanup;
     491  
     492  	if (opt_print_acl && opt_print_default_acl)
     493  		default_prefix = "default:";
     494  
     495  	if (opt_strip_leading_slash) {
     496  		if (*path_p == '/') {
     497  			if (!absolute_warning) {
     498  				fprintf(stderr, _("%s: Removing leading "
     499  					"'/' from absolute path names\n"),
     500  				        progname);
     501  				absolute_warning = 1;
     502  			}
     503  			while (*path_p == '/')
     504  				path_p++;
     505  		} else if (*path_p == '.' && *(path_p+1) == '/')
     506  			while (*++path_p == '/')
     507  				/* nothing */ ;
     508  		if (*path_p == '\0')
     509  			path_p = ".";
     510  	}
     511  
     512  	if (opt_tabular)  {
     513  		if (do_show(stdout, path_p, st, acl, default_acl) != 0)
     514  			goto fail;
     515  	} else {
     516  		if (opt_comments) {
     517  			printf("# file: %s\n", xquote(path_p, "\n\r"));
     518  			printf("# owner: %s\n",
     519  			       xquote(user_name(st->st_uid, opt_numeric), " \t\n\r"));
     520  			printf("# group: %s\n",
     521  			       xquote(group_name(st->st_gid, opt_numeric), " \t\n\r"));
     522  			if ((st->st_mode & (S_ISVTX | S_ISUID | S_ISGID)) && !posixly_correct)
     523  				printf("# flags: %s\n", flagstr(st->st_mode));
     524  		}
     525  		if (acl != NULL) {
     526  			char *acl_text = acl_to_any_text(acl, NULL, '\n',
     527  							 print_options);
     528  			if (!acl_text)
     529  				goto fail;
     530  			if (puts(acl_text) < 0) {
     531  				acl_free(acl_text);
     532  				goto fail;
     533  			}
     534  			acl_free(acl_text);
     535  		}
     536  		if (default_acl != NULL) {
     537  			char *acl_text = acl_to_any_text(default_acl, 
     538  							 default_prefix, '\n',
     539  							 print_options);
     540  			if (!acl_text)
     541  				goto fail;
     542  			if (puts(acl_text) < 0) {
     543  				acl_free(acl_text);
     544  				goto fail;
     545  			}
     546  			acl_free(acl_text);
     547  		}
     548  	}
     549  	if (acl || default_acl || opt_comments)
     550  		printf("\n");
     551  
     552  cleanup:
     553  	if (acl)
     554  		acl_free(acl);
     555  	if (default_acl)
     556  		acl_free(default_acl);
     557  	return error;
     558  
     559  fail:
     560  	fprintf(stderr, "%s: %s: %s\n", progname, xquote(path_p, "\n\r"),
     561  		strerror(errno));
     562  	error = -1;
     563  	goto cleanup;
     564  }
     565  
     566  
     567  void help(void)
     568  {
     569  	printf(_("%s %s -- get file access control lists\n"),
     570  	       progname, VERSION);
     571  	printf(_("Usage: %s [-%s] file ...\n"),
     572  	         progname, cmd_line_options);
     573  #if !POSIXLY_CORRECT
     574  	if (posixly_correct) {
     575  #endif
     576  		printf(_(
     577  "  -d, --default           display the default access control list\n"));
     578  #if !POSIXLY_CORRECT
     579  	} else {
     580  		printf(_(
     581  "  -a, --access            display the file access control list only\n"
     582  "  -d, --default           display the default access control list only\n"
     583  "  -c, --omit-header       do not display the comment header\n"
     584  "  -e, --all-effective     print all effective rights\n"
     585  "  -E, --no-effective      print no effective rights\n"
     586  "  -s, --skip-base         skip files that only have the base entries\n"
     587  "  -R, --recursive         recurse into subdirectories\n"
     588  "  -L, --logical           logical walk, follow symbolic links\n"
     589  "  -P, --physical          physical walk, do not follow symbolic links\n"
     590  "  -t, --tabular           use tabular output format\n"
     591  "  -n, --numeric           print numeric user/group identifiers\n"
     592  "      --one-file-system   skip files on different filesystems\n"
     593  "  -p, --absolute-names    don't strip leading '/' in pathnames\n"));
     594  	}
     595  #endif
     596  	printf(_(
     597  "  -v, --version           print version and exit\n"
     598  "  -h, --help              this help text\n"));
     599  }
     600  
     601  int main(int argc, char *argv[])
     602  {
     603  	int opt;
     604  	char *line;
     605  
     606  	progname = basename(argv[0]);
     607  
     608  #if POSIXLY_CORRECT
     609  	cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
     610  #else
     611  	if (getenv(POSIXLY_CORRECT_STR))
     612  		posixly_correct = 1;
     613  	if (!posixly_correct)
     614  		cmd_line_options = CMD_LINE_OPTIONS;
     615  	else
     616  		cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
     617  #endif
     618  
     619  	setlocale(LC_CTYPE, "");
     620  	setlocale(LC_MESSAGES, "");
     621  	bindtextdomain(PACKAGE, LOCALEDIR);
     622  	textdomain(PACKAGE);
     623  
     624  	/* Align `#effective:' comments to column 40 for tty's */
     625  	if (!posixly_correct && isatty(fileno(stdout)))
     626  		print_options |= TEXT_SMART_INDENT;
     627  
     628  	while ((opt = getopt_long(argc, argv, cmd_line_options,
     629  		                 long_options, NULL)) != -1) {
     630  		switch (opt) {
     631  			case 'a':  /* acl only */
     632  				if (posixly_correct)
     633  					goto synopsis;
     634  				opt_print_acl = 1;
     635  				break;
     636  
     637  			case 'd':  /* default acl only */
     638  				opt_print_default_acl = 1;
     639  				break;
     640  
     641  			case 'c':  /* no comments */
     642  				if (posixly_correct)
     643  					goto synopsis;
     644  				opt_comments = 0;
     645  				break;
     646  
     647  			case 'e':  /* all #effective comments */
     648  				if (posixly_correct)
     649  					goto synopsis;
     650  				print_options |= TEXT_ALL_EFFECTIVE;
     651  				break;
     652  
     653  			case 'E':  /* no #effective comments */
     654  				if (posixly_correct)
     655  					goto synopsis;
     656  				print_options &= ~(TEXT_SOME_EFFECTIVE |
     657  				                   TEXT_ALL_EFFECTIVE);
     658  				break;
     659  
     660  			case 'R':  /* recursive */
     661  				if (posixly_correct)
     662  					goto synopsis;
     663  				walk_flags |= WALK_TREE_RECURSIVE;
     664  				break;
     665  
     666  			case 'L':  /* follow all symlinks */
     667  				if (posixly_correct)
     668  					goto synopsis;
     669  				walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
     670  				walk_flags &= ~WALK_TREE_PHYSICAL;
     671  				break;
     672  
     673  			case 'P':  /* skip all symlinks */
     674  				if (posixly_correct)
     675  					goto synopsis;
     676  				walk_flags |= WALK_TREE_PHYSICAL;
     677  				walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
     678  						WALK_TREE_DEREFERENCE_TOPLEVEL);
     679  				break;
     680  
     681  			case 's':  /* skip files with only base entries */
     682  				if (posixly_correct)
     683  					goto synopsis;
     684  				opt_skip_base = 1;
     685  				break;
     686  
     687  			case 'p':
     688  				if (posixly_correct)
     689  					goto synopsis;
     690  				opt_strip_leading_slash = 0;
     691  				break;
     692  
     693  			case 't':
     694  				if (posixly_correct)
     695  					goto synopsis;
     696  				opt_tabular = 1;
     697  				break;
     698  
     699  			case 'n':  /* numeric */
     700  				opt_numeric = 1;
     701  				print_options |= TEXT_NUMERIC_IDS;
     702  				break;
     703  
     704  			case 1: /* one filesystem */
     705  				walk_flags |= WALK_TREE_ONE_FILESYSTEM;
     706  				break;
     707  
     708  			case 'v':  /* print version */
     709  				printf("%s " VERSION "\n", progname);
     710  				return 0;
     711  
     712  			case 'h':  /* help */
     713  				help();
     714  				return 0;
     715  
     716  			case ':':  /* option missing */
     717  			case '?':  /* unknown option */
     718  			default:
     719  				goto synopsis;
     720  		}
     721  	}
     722  
     723  	if (!(opt_print_acl || opt_print_default_acl)) {
     724  		opt_print_acl = 1;
     725  		if (!posixly_correct)
     726  			opt_print_default_acl = 1;
     727  	}
     728  		
     729  	if ((optind == argc) && !posixly_correct)
     730  		goto synopsis;
     731  
     732  	do {
     733  		if (optind == argc ||
     734  		    strcmp(argv[optind], "-") == 0) {
     735  			while ((line = __acl_next_line(stdin)) != NULL) {
     736  				if (*line == '\0')
     737  					continue;
     738  
     739  				had_errors += walk_tree(line, walk_flags, 0,
     740  							do_print, NULL);
     741  			}
     742  			if (!feof(stdin)) {
     743  				fprintf(stderr, _("%s: Standard input: %s\n"),
     744  				        progname, strerror(errno));
     745  				had_errors++;
     746  			}
     747  		} else
     748  			had_errors += walk_tree(argv[optind], walk_flags, 0,
     749  						do_print, NULL);
     750  		optind++;
     751  	} while (optind < argc);
     752  
     753  	return had_errors ? 1 : 0;
     754  
     755  synopsis:
     756  	fprintf(stderr, _("Usage: %s [-%s] file ...\n"),
     757  	        progname, cmd_line_options);
     758  	fprintf(stderr, _("Try `%s --help' for more information.\n"),
     759  		progname);
     760  	return 2;
     761  }
     762