(root)/
acl-2.3.1/
tools/
setfacl.c
       1  /*
       2    File: setfacl.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
       9    modify it under the terms of the GNU Lesser General Public
      10    License as published by the Free Software Foundation; either
      11    version 2.1 of the License, or (at your option) any later version.
      12  
      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 GNU
      16    Lesser General Public License for more details.
      17  
      18    You should have received a copy of the GNU Lesser General Public
      19    License along with this library; if not, write to the Free Software
      20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
      21  */
      22  
      23  #include "config.h"
      24  #include <limits.h>
      25  #include <stdio.h>
      26  #include <string.h>
      27  #include <unistd.h>
      28  #include <errno.h>
      29  #include <sys/stat.h>
      30  #include <dirent.h>
      31  #include <libgen.h>
      32  #include <getopt.h>
      33  #include "misc.h"
      34  #include "sequence.h"
      35  #include "parse.h"
      36  #include "do_set.h"
      37  #include "walk_tree.h"
      38  
      39  #define POSIXLY_CORRECT_STR "POSIXLY_CORRECT"
      40  
      41  /* '-' stands for `process non-option arguments in loop' */
      42  #if !POSIXLY_CORRECT
      43  #  define CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:RLP"
      44  #  define CMD_LINE_SPEC "[-bkndRLP] { -m|-M|-x|-X ... } file ..."
      45  #endif
      46  #define POSIXLY_CMD_LINE_OPTIONS "-:bkndvhm:M:x:X:"
      47  #define POSIXLY_CMD_LINE_SPEC "[-bknd] {-m|-M|-x|-X ... } file ..."
      48  
      49  struct option long_options[] = {
      50  #if !POSIXLY_CORRECT
      51  	{ "set",		1, 0, 's' },
      52  	{ "set-file",		1, 0, 'S' },
      53  
      54  	{ "mask",		0, 0, 'r' },
      55  	{ "recursive",		0, 0, 'R' },
      56  	{ "logical",		0, 0, 'L' },
      57  	{ "physical",		0, 0, 'P' },
      58  	{ "restore",		1, 0, 'B' },
      59  	{ "test",		0, 0, 't' },
      60  #endif
      61  	{ "modify",		1, 0, 'm' },
      62  	{ "modify-file",	1, 0, 'M' },
      63  	{ "remove",		1, 0, 'x' },
      64  	{ "remove-file",	1, 0, 'X' },
      65  
      66  	{ "default",		0, 0, 'd' },
      67  	{ "no-mask",		0, 0, 'n' },
      68  	{ "remove-all",		0, 0, 'b' },
      69  	{ "remove-default",	0, 0, 'k' },
      70  	{ "version",		0, 0, 'v' },
      71  	{ "help",		0, 0, 'h' },
      72  	{ NULL,			0, 0, 0   },
      73  };
      74  
      75  const char *progname;
      76  const char *cmd_line_options, *cmd_line_spec;
      77  
      78  int walk_flags = WALK_TREE_DEREFERENCE_TOPLEVEL;
      79  int opt_recalculate;  /* recalculate mask entry (0=default, 1=yes, -1=no) */
      80  int opt_promote;  /* promote access ACL to default ACL */
      81  int opt_test;  /* do not write to the file system.
      82                        Print what would happen instead. */
      83  #if POSIXLY_CORRECT
      84  const int posixly_correct = 1;  /* Posix compatible behavior! */
      85  #else
      86  int posixly_correct;  /* Posix compatible behavior? */
      87  #endif
      88  int chown_error;
      89  int promote_warning;
      90  
      91  
      92  static const char *xquote(const char *str, const char *quote_chars)
      93  {
      94  	const char *q = __acl_quote(str, quote_chars);
      95  	if (q == NULL) {
      96  		fprintf(stderr, "%s: %s\n", progname, strerror(errno));
      97  		exit(1);
      98  	}
      99  	return q;
     100  }
     101  
     102  int
     103  has_any_of_type(
     104  	cmd_t cmd,
     105  	acl_type_t acl_type)
     106  {
     107  	while (cmd) {
     108  		if (cmd->c_type == acl_type)
     109  			return 1;
     110  		cmd = cmd->c_next;
     111  	}
     112  	return 0;
     113  }
     114  	
     115  
     116  #if !POSIXLY_CORRECT
     117  int
     118  restore(
     119  	FILE *file,
     120  	const char *filename)
     121  {
     122  	char *path_p;
     123  	struct stat st;
     124  	uid_t uid;
     125  	gid_t gid;
     126  	mode_t mask, flags;
     127  	struct do_set_args args = { };
     128  	int lineno = 0, backup_line;
     129  	int error, status = 0;
     130  	int chmod_required = 0;
     131  
     132  	memset(&st, 0, sizeof(st));
     133  
     134  	for(;;) {
     135  		backup_line = lineno;
     136  		error = read_acl_comments(file, &lineno, &path_p, &uid, &gid,
     137  					  &flags);
     138  		if (error < 0) {
     139  			error = -error;
     140  			goto fail;
     141  		}
     142  		if (error == 0)
     143  			return status;
     144  
     145  		if (path_p == NULL) {
     146  			if (filename) {
     147  				fprintf(stderr, _("%s: %s: No filename found "
     148  						  "in line %d, aborting\n"),
     149  					progname, xquote(filename, "\n\r"),
     150  					backup_line);
     151  			} else {
     152  				fprintf(stderr, _("%s: No filename found in "
     153  						 "line %d of standard input, "
     154  						 "aborting\n"),
     155  					progname, backup_line);
     156  			}
     157  			status = 1;
     158  			goto getout;
     159  		}
     160  
     161  		if (!(args.seq = seq_init()))
     162  			goto fail_errno;
     163  		if (seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_ACCESS) ||
     164  		    seq_append_cmd(args.seq, CMD_REMOVE_ACL, ACL_TYPE_DEFAULT))
     165  			goto fail_errno;
     166  
     167  		error = read_acl_seq(file, args.seq, CMD_ENTRY_REPLACE,
     168  		                     SEQ_PARSE_WITH_PERM |
     169  				     SEQ_PARSE_DEFAULT |
     170  				     SEQ_PARSE_MULTI,
     171  				     &lineno, NULL);
     172  		if (error != 0) {
     173  			fprintf(stderr, _("%s: %s: %s in line %d\n"),
     174  			        progname, xquote(filename, "\n\r"), strerror(errno),
     175  				lineno);
     176  			status = 1;
     177  			goto getout;
     178  		}
     179  
     180  		error = stat(path_p, &st);
     181  		if (opt_test && error != 0) {
     182  			fprintf(stderr, "%s: %s: %s\n", progname,
     183  				xquote(path_p, "\n\r"), strerror(errno));
     184  			status = 1;
     185  		}
     186  
     187  		args.mode = 0;
     188  		error = do_set(path_p, &st, 0, &args);
     189  		if (error != 0) {
     190  			status = 1;
     191  			goto resume;
     192  		}
     193  
     194  		if (uid != ACL_UNDEFINED_ID && uid != st.st_uid)
     195  			st.st_uid = uid;
     196  		else
     197  			st.st_uid = -1;
     198  		if (gid != ACL_UNDEFINED_ID && gid != st.st_gid)
     199  			st.st_gid = gid;
     200  		else
     201  			st.st_gid = -1;
     202  		if (!opt_test &&
     203  		    (st.st_uid != -1 || st.st_gid != -1)) {
     204  			if (chown(path_p, st.st_uid, st.st_gid) != 0) {
     205  				fprintf(stderr, _("%s: %s: Cannot change "
     206  					          "owner/group: %s\n"),
     207  					progname, xquote(path_p, "\n\r"),
     208  					strerror(errno));
     209  				status = 1;
     210  			}
     211  
     212  			/* chown() clears setuid/setgid so force a chmod if
     213  			 * S_ISUID/S_ISGID was expected */
     214  			if ((st.st_mode & flags) & (S_ISUID | S_ISGID))
     215  				chmod_required = 1;
     216  		}
     217  
     218  		mask = S_ISUID | S_ISGID | S_ISVTX;
     219  		if (chmod_required || ((st.st_mode & mask) != (flags & mask))) {
     220  			if (!args.mode)
     221  				args.mode = st.st_mode;
     222  			args.mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
     223  			if (chmod(path_p, flags | args.mode) != 0) {
     224  				fprintf(stderr, _("%s: %s: Cannot change "
     225  					          "mode: %s\n"),
     226  					progname, xquote(path_p, "\n\r"),
     227  					strerror(errno));
     228  				status = 1;
     229  			}
     230  		}
     231  resume:
     232  		if (path_p) {
     233  			free(path_p);
     234  			path_p = NULL;
     235  		}
     236  		if (args.seq) {
     237  			seq_free(args.seq);
     238  			args.seq = NULL;
     239  		}
     240  	}
     241  
     242  getout:
     243  	if (path_p) {
     244  		free(path_p);
     245  		path_p = NULL;
     246  	}
     247  	if (args.seq) {
     248  		seq_free(args.seq);
     249  		args.seq = NULL;
     250  	}
     251  	return status;
     252  
     253  fail_errno:
     254  	error = errno;
     255  fail:
     256  	fprintf(stderr, "%s: %s: %s\n", progname, xquote(filename, "\n\r"),
     257  		strerror(error));
     258  	status = 1;
     259  	goto getout;
     260  }
     261  #endif
     262  
     263  
     264  void help(void)
     265  {
     266  	printf(_("%s %s -- set file access control lists\n"),
     267  		progname, VERSION);
     268  	printf(_("Usage: %s %s\n"),
     269  		progname, cmd_line_spec);
     270  	printf(_(
     271  "  -m, --modify=acl        modify the current ACL(s) of file(s)\n"
     272  "  -M, --modify-file=file  read ACL entries to modify from file\n"
     273  "  -x, --remove=acl        remove entries from the ACL(s) of file(s)\n"
     274  "  -X, --remove-file=file  read ACL entries to remove from file\n"
     275  "  -b, --remove-all        remove all extended ACL entries\n"
     276  "  -k, --remove-default    remove the default ACL\n"));
     277  #if !POSIXLY_CORRECT
     278  	if (!posixly_correct) {
     279  		printf(_(
     280  "      --set=acl           set the ACL of file(s), replacing the current ACL\n"
     281  "      --set-file=file     read ACL entries to set from file\n"
     282  "      --mask              do recalculate the effective rights mask\n"));
     283  	}
     284  #endif
     285    	printf(_(
     286  "  -n, --no-mask           don't recalculate the effective rights mask\n"
     287  "  -d, --default           operations apply to the default ACL\n"));
     288  #if !POSIXLY_CORRECT
     289  	if (!posixly_correct) {
     290  		printf(_(
     291  "  -R, --recursive         recurse into subdirectories\n"
     292  "  -L, --logical           logical walk, follow symbolic links\n"
     293  "  -P, --physical          physical walk, do not follow symbolic links\n"
     294  "      --restore=file      restore ACLs (inverse of `getfacl -R')\n"
     295  "      --test              test mode (ACLs are not modified)\n"));
     296  	}
     297  #endif
     298  	printf(_(
     299  "  -v, --version           print version and exit\n"
     300  "  -h, --help              this help text\n"));
     301  }
     302  
     303  
     304  int next_file(const char *arg, seq_t seq)
     305  {
     306  	char *line;
     307  	int errors = 0;
     308  	struct do_set_args args;
     309  
     310  	args.seq = seq;
     311  
     312  	if (strcmp(arg, "-") == 0) {
     313  		while ((line = __acl_next_line(stdin)))
     314  			errors = walk_tree(line, walk_flags, 0, do_set, &args);
     315  		if (!feof(stdin)) {
     316  			fprintf(stderr, _("%s: Standard input: %s\n"),
     317  				progname, strerror(errno));
     318  			errors = 1;
     319  		}
     320  	} else {
     321  		errors = walk_tree(arg, walk_flags, 0, do_set, &args);
     322  	}
     323  	return errors ? 1 : 0;
     324  }
     325  
     326  
     327  #define ERRNO_ERROR(s) \
     328  	({status = (s); goto errno_error; })
     329  
     330  
     331  int main(int argc, char *argv[])
     332  {
     333  	int opt;
     334  	int saw_files = 0;
     335  	int status = 0;
     336  	FILE *file;
     337  	int which;
     338  	int lineno;
     339  	int error;
     340  	seq_t seq;
     341  	int seq_cmd, parse_mode;
     342  	
     343  	progname = basename(argv[0]);
     344  
     345  #if POSIXLY_CORRECT
     346  	cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
     347  	cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
     348  #else
     349  	if (getenv(POSIXLY_CORRECT_STR))
     350  		posixly_correct = 1;
     351  	if (!posixly_correct) {
     352  		cmd_line_options = CMD_LINE_OPTIONS;
     353  		cmd_line_spec = _(CMD_LINE_SPEC);
     354  	} else {
     355  		cmd_line_options = POSIXLY_CMD_LINE_OPTIONS;
     356  		cmd_line_spec = _(POSIXLY_CMD_LINE_SPEC);
     357  	}
     358  #endif
     359  
     360  	setlocale(LC_CTYPE, "");
     361  	setlocale(LC_MESSAGES, "");
     362  	bindtextdomain(PACKAGE, LOCALEDIR);
     363  	textdomain(PACKAGE);
     364  
     365  	seq = seq_init();
     366  	if (!seq)
     367  		ERRNO_ERROR(1);
     368  
     369  	while ((opt = getopt_long(argc, argv, cmd_line_options,
     370  		                  long_options, NULL)) != -1) {
     371  		/* we remember the two REMOVE_ACL commands of the set
     372  		   operations because we may later need to delete them.  */
     373  		cmd_t seq_remove_default_acl_cmd = NULL;
     374  		cmd_t seq_remove_acl_cmd = NULL;
     375  
     376  		if (opt != '\1' && saw_files) {
     377  			seq_free(seq);
     378  			seq = seq_init();
     379  			if (!seq)
     380  				ERRNO_ERROR(1);
     381  			saw_files = 0;
     382  		}
     383  
     384  		switch (opt) {
     385  			case 'b':  /* remove all extended entries */
     386  				if (seq_append_cmd(seq, CMD_REMOVE_EXTENDED_ACL,
     387  				                        ACL_TYPE_ACCESS) ||
     388  				    seq_append_cmd(seq, CMD_REMOVE_ACL,
     389  				                        ACL_TYPE_DEFAULT))
     390  					ERRNO_ERROR(1);
     391  				break;
     392  
     393  			case 'k':  /* remove default ACL */
     394  				if (seq_append_cmd(seq, CMD_REMOVE_ACL,
     395  				                        ACL_TYPE_DEFAULT))
     396  					ERRNO_ERROR(1);
     397  				break;
     398  
     399  			case 'n':  /* do not recalculate mask */
     400  				opt_recalculate = -1;
     401  				break;
     402  
     403  			case 'r':  /* force recalculate mask */
     404  				opt_recalculate = 1;
     405  				break;
     406  
     407  			case 'd':  /*  operations apply to default ACL */
     408  				opt_promote = 1;
     409  				break;
     410  
     411  			case 's':  /* set */
     412  				if (seq_append_cmd(seq, CMD_REMOVE_ACL,
     413  					                ACL_TYPE_ACCESS))
     414  					ERRNO_ERROR(1);
     415  				seq_remove_acl_cmd = seq->s_last;
     416  				if (seq_append_cmd(seq, CMD_REMOVE_ACL,
     417  				                        ACL_TYPE_DEFAULT))
     418  					ERRNO_ERROR(1);
     419  				seq_remove_default_acl_cmd = seq->s_last;
     420  
     421  				seq_cmd = CMD_ENTRY_REPLACE;
     422  				parse_mode = SEQ_PARSE_WITH_PERM;
     423  				goto set_modify_delete;
     424  
     425  			case 'm':  /* modify */
     426  				seq_cmd = CMD_ENTRY_REPLACE;
     427  				parse_mode = SEQ_PARSE_WITH_PERM;
     428  				goto set_modify_delete;
     429  
     430  			case 'x':  /* delete */
     431  				seq_cmd = CMD_REMOVE_ENTRY;
     432  #if POSIXLY_CORRECT
     433  				parse_mode = SEQ_PARSE_ANY_PERM;
     434  #else
     435  				if (posixly_correct)
     436  					parse_mode = SEQ_PARSE_ANY_PERM;
     437  				else
     438  					parse_mode = SEQ_PARSE_NO_PERM;
     439  #endif
     440  				goto set_modify_delete;
     441  
     442  			set_modify_delete:
     443  				if (!posixly_correct)
     444  					parse_mode |= SEQ_PARSE_DEFAULT;
     445  				if (opt_promote)
     446  					parse_mode |= SEQ_PROMOTE_ACL;
     447  				if (parse_acl_seq(seq, optarg, &which,
     448  				                  seq_cmd, parse_mode) != 0) {
     449  					if (which < 0 ||
     450  					    (size_t) which >= strlen(optarg)) {
     451  						fprintf(stderr, _(
     452  							"%s: Option "
     453  						        "-%c incomplete\n"),
     454  							progname, opt);
     455  					} else {
     456  						fprintf(stderr, _(
     457  							"%s: Option "
     458  						        "-%c: %s near "
     459  							"character %d\n"),
     460  							progname, opt,
     461  							strerror(errno),
     462  							which+1);
     463  					}
     464  					status = 2;
     465  					goto cleanup;
     466  				}
     467  				break;
     468  
     469  			case 'S':  /* set from file */
     470  				if (seq_append_cmd(seq, CMD_REMOVE_ACL,
     471  					                ACL_TYPE_ACCESS))
     472  					ERRNO_ERROR(1);
     473  				seq_remove_acl_cmd = seq->s_last;
     474  				if (seq_append_cmd(seq, CMD_REMOVE_ACL,
     475  				                        ACL_TYPE_DEFAULT))
     476  					ERRNO_ERROR(1);
     477  				seq_remove_default_acl_cmd = seq->s_last;
     478  
     479  				seq_cmd = CMD_ENTRY_REPLACE;
     480  				parse_mode = SEQ_PARSE_WITH_PERM;
     481  				goto set_modify_delete_from_file;
     482  
     483  			case 'M':  /* modify from file */
     484  				seq_cmd = CMD_ENTRY_REPLACE;
     485  				parse_mode = SEQ_PARSE_WITH_PERM;
     486  				goto set_modify_delete_from_file;
     487  
     488  			case 'X':  /* delete from file */
     489  				seq_cmd = CMD_REMOVE_ENTRY;
     490  #if POSIXLY_CORRECT
     491  				parse_mode = SEQ_PARSE_ANY_PERM;
     492  #else
     493  				if (posixly_correct)
     494  					parse_mode = SEQ_PARSE_ANY_PERM;
     495  				else
     496  					parse_mode = SEQ_PARSE_NO_PERM;
     497  #endif
     498  				goto set_modify_delete_from_file;
     499  
     500  			set_modify_delete_from_file:
     501  				if (!posixly_correct)
     502  					parse_mode |= SEQ_PARSE_DEFAULT;
     503  				if (opt_promote)
     504  					parse_mode |= SEQ_PROMOTE_ACL;
     505  				if (strcmp(optarg, "-") == 0) {
     506  					file = stdin;
     507  				} else {
     508  					file = fopen(optarg, "r");
     509  					if (file == NULL) {
     510  						fprintf(stderr, "%s: %s: %s\n",
     511  							progname,
     512  							xquote(optarg, "\n\r"),
     513  							strerror(errno));
     514  						status = 2;
     515  						goto cleanup;
     516  					}
     517  				}
     518  
     519  				lineno = 0;
     520  				error = read_acl_seq(file, seq, seq_cmd,
     521  				                     parse_mode, &lineno, NULL);
     522  				
     523  				if (file != stdin) {
     524  					fclose(file);
     525  				}
     526  
     527  				if (error) {
     528  					if (!errno)
     529  						errno = EINVAL;
     530  
     531  					if (file != stdin) {
     532  						fprintf(stderr, _(
     533  							"%s: %s in line "
     534  						        "%d of file %s\n"),
     535  							progname,
     536  							strerror(errno),
     537  							lineno,
     538  							xquote(optarg, "\n\r"));
     539  					} else {
     540  						fprintf(stderr, _(
     541  							"%s: %s in line "
     542  						        "%d of standard "
     543  							"input\n"), progname,
     544  							strerror(errno),
     545  							lineno);
     546  					}
     547  					status = 2;
     548  					goto cleanup;
     549  				}
     550  				break;
     551  
     552  
     553  			case '\1':  /* file argument */
     554  				if (seq_empty(seq))
     555  					goto synopsis;
     556  				saw_files = 1;
     557  
     558  				status = next_file(optarg, seq);
     559  				break;
     560  
     561  			case 'B':  /* restore ACL backup */
     562  				saw_files = 1;
     563  
     564  				if (strcmp(optarg, "-") == 0)
     565  					file = stdin;
     566  				else {
     567  					file = fopen(optarg, "r");
     568  					if (file == NULL) {
     569  						fprintf(stderr, "%s: %s: %s\n",
     570  							progname,
     571  							xquote(optarg, "\n\r"),
     572  							strerror(errno));
     573  						status = 2;
     574  						goto cleanup;
     575  					}
     576  				}
     577  
     578  				status = restore(file,
     579  				               (file == stdin) ? NULL : optarg);
     580  
     581  				if (file != stdin)
     582  					fclose(file);
     583  				if (status != 0)
     584  					goto cleanup;
     585  				break;
     586  
     587  			case 'R':  /* recursive */
     588  				walk_flags |= WALK_TREE_RECURSIVE;
     589  				break;
     590  
     591  			case 'L':  /* follow symlinks */
     592  				walk_flags |= WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE;
     593  				walk_flags &= ~WALK_TREE_PHYSICAL;
     594  				break;
     595  
     596  			case 'P':  /* do not follow symlinks */
     597  				walk_flags |= WALK_TREE_PHYSICAL;
     598  				walk_flags &= ~(WALK_TREE_LOGICAL | WALK_TREE_DEREFERENCE |
     599  						WALK_TREE_DEREFERENCE_TOPLEVEL);
     600  				break;
     601  
     602  			case 't':  /* test mode */
     603  				opt_test = 1;
     604  				break;
     605  
     606  			case 'v':  /* print version and exit */
     607  				printf("%s " VERSION "\n", progname);
     608  				status = 0;
     609  				goto cleanup;
     610  
     611  			case 'h':  /* help! */
     612  				help();
     613  				status = 0;
     614  				goto cleanup;
     615  
     616  			case ':':  /* option missing */
     617  			case '?':  /* unknown option */
     618  			default:
     619  				goto synopsis;
     620  		}
     621  		if (seq_remove_acl_cmd) {
     622  			/* This was a set operation. Check if there are
     623  			   actually entries of ACL_TYPE_ACCESS; if there
     624  			   are none, we need to remove this command! */
     625  			if (!has_any_of_type(seq_remove_acl_cmd->c_next,
     626  				            ACL_TYPE_ACCESS))
     627  				seq_delete_cmd(seq, seq_remove_acl_cmd);
     628  		}
     629  		if (seq_remove_default_acl_cmd) {
     630  			/* This was a set operation. Check if there are
     631  			   actually entries of ACL_TYPE_DEFAULT; if there
     632  			   are none, we need to remove this command! */
     633  			if (!has_any_of_type(seq_remove_default_acl_cmd->c_next,
     634  				            ACL_TYPE_DEFAULT))
     635  				seq_delete_cmd(seq, seq_remove_default_acl_cmd);
     636  		}
     637  	}
     638  	while (optind < argc) {
     639  		if(!seq)
     640  			goto synopsis;
     641  		if (seq_empty(seq))
     642  			goto synopsis;
     643  		saw_files = 1;
     644  
     645  		status = next_file(argv[optind++], seq);
     646  	}
     647  	if (!saw_files)
     648  		goto synopsis;
     649  
     650  	goto cleanup;
     651  
     652  synopsis:
     653  	fprintf(stderr, _("Usage: %s %s\n"),
     654  		progname, cmd_line_spec);
     655  	fprintf(stderr, _("Try `%s --help' for more information.\n"),
     656  		progname);
     657  	status = 2;
     658  	goto cleanup;
     659  
     660  errno_error:
     661  	fprintf(stderr, "%s: %s\n", progname, strerror(errno));
     662  	goto cleanup;
     663  
     664  cleanup:
     665  	if (seq)
     666  		seq_free(seq);
     667  	return status;
     668  }
     669