From 9288068ff8f3f749edc54c50f064eca9e6c83019 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sun, 10 Apr 2011 11:22:59 +0200 Subject: [PATCH] maint: reorder install.c to eliminate declarations of static functions * src/install.c: Remove static function declarations. --- src/install.c | 957 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 470 insertions(+), 487 deletions(-) diff --git a/src/install.c b/src/install.c index f416996..369547b 100644 --- a/src/install.c +++ b/src/install.c @@ -68,23 +68,6 @@ static bool use_default_selinux_context = true; # define matchpathcon_init_prefix(a, p) /* empty */ #endif -static bool change_timestamps (struct stat const *from_sb, char const *to); -static bool change_attributes (char const *name); -static bool copy_file (const char *from, const char *to, - const struct cp_options *x); -static bool install_file_in_file_parents (char const *from, char *to, - struct cp_options *x); -static bool install_file_in_dir (const char *from, const char *to_dir, - const struct cp_options *x); -static bool install_file_in_file (const char *from, const char *to, - const struct cp_options *x); -static void get_ids (void); -static void strip (char const *name); -static void announce_mkdir (char const *dir, void *options); -static int make_ancestor (char const *dir, char const *component, - void *options); -void usage (int status); - /* The user name that will own the files, or NULL to make the owner the current user ID. */ static char *owner_name; @@ -407,6 +390,27 @@ target_directory_operand (char const *file) return is_a_dir; } +/* Report that directory DIR was made, if OPTIONS requests this. */ +static void +announce_mkdir (char const *dir, void *options) +{ + struct cp_options const *x = options; + if (x->verbose) + prog_fprintf (stdout, _("creating directory %s"), quote (dir)); +} + +/* Make ancestor directory DIR, whose last file name component is + COMPONENT, with options OPTIONS. Assume the working directory is + COMPONENT's parent. */ +static int +make_ancestor (char const *dir, char const *component, void *options) +{ + int r = mkdir (component, DEFAULT_MODE); + if (r == 0) + announce_mkdir (dir, options); + return r; +} + /* Process a command-line file name, for the -d option. */ static int process_dir (char *dir, struct savewd *wd, void *options) @@ -419,267 +423,256 @@ process_dir (char *dir, struct savewd *wd, void *options) : EXIT_FAILURE); } -int -main (int argc, char **argv) -{ - int optc; - int exit_status = EXIT_SUCCESS; - const char *specified_mode = NULL; - bool make_backups = false; - char *backup_suffix_string; - char *version_control_string = NULL; - bool mkdir_and_install = false; - struct cp_options x; - char const *target_directory = NULL; - bool no_target_directory = false; - int n_files; - char **file; - bool strip_program_specified = false; - security_context_t scontext = NULL; - /* set iff kernel has extra selinux system calls */ - selinux_enabled = (0 < is_selinux_enabled ()); - - initialize_main (&argc, &argv); - set_program_name (argv[0]); - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); +/* Copy file FROM onto file TO, creating TO if necessary. + Return true if successful. */ - atexit (close_stdin); +static bool +copy_file (const char *from, const char *to, const struct cp_options *x) +{ + bool copy_into_self; - cp_option_init (&x); + if (copy_only_if_needed && !need_copy (from, to, x)) + return true; - owner_name = NULL; - group_name = NULL; - strip_files = false; - dir_arg = false; - umask (0); + /* Allow installing from non-regular files like /dev/null. + Charles Karney reported that some Sun version of install allows that + and that sendmail's installation process relies on the behavior. + However, since !x->recursive, the call to "copy" will fail if FROM + is a directory. */ - /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless - we'll actually use backup_suffix_string. */ - backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); + return copy (from, to, false, x, ©_into_self, NULL); +} - while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options, - NULL)) != -1) - { - switch (optc) - { - case 'b': - make_backups = true; - if (optarg) - version_control_string = optarg; - break; - case 'c': - break; - case 'C': - copy_only_if_needed = true; - break; - case 's': - strip_files = true; -#ifdef SIGCHLD - /* System V fork+wait does not work if SIGCHLD is ignored. */ - signal (SIGCHLD, SIG_DFL); -#endif - break; - case STRIP_PROGRAM_OPTION: - strip_program = xstrdup (optarg); - strip_program_specified = true; - break; - case 'd': - dir_arg = true; - break; - case 'D': - mkdir_and_install = true; - break; - case 'v': - x.verbose = true; - break; - case 'g': - group_name = optarg; - break; - case 'm': - specified_mode = optarg; - break; - case 'o': - owner_name = optarg; - break; - case 'p': - x.preserve_timestamps = true; - break; - case 'S': - make_backups = true; - backup_suffix_string = optarg; - break; - case 't': - if (target_directory) - error (EXIT_FAILURE, 0, - _("multiple target directories specified")); - else - { - struct stat st; - if (stat (optarg, &st) != 0) - error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg)); - if (! S_ISDIR (st.st_mode)) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (optarg)); - } - target_directory = optarg; - break; - case 'T': - no_target_directory = true; - break; +/* Set the attributes of file or directory NAME. + Return true if successful. */ - case PRESERVE_CONTEXT_OPTION_DEPRECATED: - error (0, 0, _("WARNING: --preserve_context is deprecated; " - "use --preserve-context instead")); - /* fall through */ - case PRESERVE_CONTEXT_OPTION: - if ( ! selinux_enabled) - { - error (0, 0, _("WARNING: ignoring --preserve-context; " - "this kernel is not SELinux-enabled")); - break; - } - x.preserve_security_context = true; - use_default_selinux_context = false; - break; - case 'Z': - if ( ! selinux_enabled) - { - error (0, 0, _("WARNING: ignoring --context (-Z); " - "this kernel is not SELinux-enabled")); - break; - } - scontext = optarg; - use_default_selinux_context = false; - break; - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: - usage (EXIT_FAILURE); - } - } +static bool +change_attributes (char const *name) +{ + bool ok = false; + /* chown must precede chmod because on some systems, + chown clears the set[ug]id bits for non-superusers, + resulting in incorrect permissions. + On System V, users can give away files with chown and then not + be able to chmod them. So don't give files away. - /* Check for invalid combinations of arguments. */ - if (dir_arg && strip_files) - error (EXIT_FAILURE, 0, - _("the strip option may not be used when installing a directory")); - if (dir_arg && target_directory) - error (EXIT_FAILURE, 0, - _("target directory not allowed when installing a directory")); + We don't normally ignore errors from chown because the idea of + the install command is that the file is supposed to end up with + precisely the attributes that the user specified (or defaulted). + If the file doesn't end up with the group they asked for, they'll + want to know. */ - if (x.preserve_security_context && scontext != NULL) - error (EXIT_FAILURE, 0, - _("cannot force target context to %s and preserve it"), - quote (scontext)); + if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1) + && lchown (name, owner_id, group_id) != 0) + error (0, errno, _("cannot change ownership of %s"), quote (name)); + else if (chmod (name, mode) != 0) + error (0, errno, _("cannot change permissions of %s"), quote (name)); + else + ok = true; - if (backup_suffix_string) - simple_backup_suffix = xstrdup (backup_suffix_string); + if (use_default_selinux_context) + setdefaultfilecon (name); - x.backup_type = (make_backups - ? xget_version (_("backup type"), - version_control_string) - : no_backups); + return ok; +} - if (scontext && setfscreatecon (scontext) < 0) - error (EXIT_FAILURE, errno, - _("failed to set default file creation context to %s"), - quote (scontext)); +/* Set the timestamps of file DEST to match those of SRC_SB. + Return true if successful. */ - n_files = argc - optind; - file = argv + optind; +static bool +change_timestamps (struct stat const *src_sb, char const *dest) +{ + struct timespec timespec[2]; + timespec[0] = get_stat_atime (src_sb); + timespec[1] = get_stat_mtime (src_sb); - if (n_files <= ! (dir_arg || target_directory)) + if (utimens (dest, timespec)) { - if (n_files <= 0) - error (0, 0, _("missing file operand")); - else - error (0, 0, _("missing destination file operand after %s"), - quote (file[0])); - usage (EXIT_FAILURE); + error (0, errno, _("cannot set time stamps for %s"), quote (dest)); + return false; } + return true; +} - if (no_target_directory) +/* Strip the symbol table from the file NAME. + We could dig the magic number out of the file first to + determine whether to strip it, but the header files and + magic numbers vary so much from system to system that making + it portable would be very difficult. Not worth the effort. */ + +static void +strip (char const *name) +{ + int status; + pid_t pid = fork (); + + switch (pid) { - if (target_directory) - error (EXIT_FAILURE, 0, - _("cannot combine --target-directory (-t) " - "and --no-target-directory (-T)")); - if (2 < n_files) - { - error (0, 0, _("extra operand %s"), quote (file[2])); - usage (EXIT_FAILURE); - } - } - else if (! (dir_arg || target_directory)) - { - if (2 <= n_files && target_directory_operand (file[n_files - 1])) - target_directory = file[--n_files]; - else if (2 < n_files) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (file[n_files - 1])); + case -1: + error (EXIT_FAILURE, errno, _("fork system call failed")); + break; + case 0: /* Child. */ + execlp (strip_program, strip_program, name, NULL); + error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program); + break; + default: /* Parent. */ + if (waitpid (pid, &status, 0) < 0) + error (EXIT_FAILURE, errno, _("waiting for strip")); + else if (! WIFEXITED (status) || WEXITSTATUS (status)) + error (EXIT_FAILURE, 0, _("strip process terminated abnormally")); + break; } +} - if (specified_mode) - { - struct mode_change *change = mode_compile (specified_mode); - if (!change) - error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode)); - mode = mode_adjust (0, false, 0, change, NULL); - dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits); - free (change); - } +/* Initialize the user and group ownership of the files to install. */ - if (strip_program_specified && !strip_files) - error (0, 0, _("WARNING: ignoring --strip-program option as -s option was " - "not specified")); +static void +get_ids (void) +{ + struct passwd *pw; + struct group *gr; - if (copy_only_if_needed && x.preserve_timestamps) + if (owner_name) { - error (0, 0, _("options --compare (-C) and --preserve-timestamps are " - "mutually exclusive")); - usage (EXIT_FAILURE); + pw = getpwnam (owner_name); + if (pw == NULL) + { + unsigned long int tmp; + if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK + || UID_T_MAX < tmp) + error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name)); + owner_id = tmp; + } + else + owner_id = pw->pw_uid; + endpwent (); } + else + owner_id = (uid_t) -1; - if (copy_only_if_needed && strip_files) + if (group_name) { - error (0, 0, _("options --compare (-C) and --strip are mutually " - "exclusive")); - usage (EXIT_FAILURE); + gr = getgrnam (group_name); + if (gr == NULL) + { + unsigned long int tmp; + if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK + || GID_T_MAX < tmp) + error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name)); + group_id = tmp; + } + else + group_id = gr->gr_gid; + endgrent (); } + else + group_id = (gid_t) -1; +} - if (copy_only_if_needed && extra_mode (mode)) - error (0, 0, _("the --compare (-C) option is ignored when you" - " specify a mode with non-permission bits")); - - get_ids (); - - if (dir_arg) - exit_status = savewd_process_files (n_files, file, process_dir, &x); +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); else { - /* FIXME: it's a little gross that this initialization is - required by copy.c::copy. */ - hash_init (); + printf (_("\ +Usage: %s [OPTION]... [-T] SOURCE DEST\n\ + or: %s [OPTION]... SOURCE... DIRECTORY\n\ + or: %s [OPTION]... -t DIRECTORY SOURCE...\n\ + or: %s [OPTION]... -d DIRECTORY...\n\ +"), + program_name, program_name, program_name, program_name); + fputs (_("\ +\n\ +This install program copies files (often just compiled) into destination\n\ +locations you choose. If you want to download and install a ready-to-use\n\ +package on a GNU/Linux system, you should instead be using a package manager\n\ +like yum(1) or apt-get(1).\n\ +\n\ +In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\ +the existing DIRECTORY, while setting permission modes and owner/group.\n\ +In the 4th form, create all components of the given DIRECTORY(ies).\n\ +\n\ +"), stdout); + fputs (_("\ +Mandatory arguments to long options are mandatory for short options too.\n\ +"), stdout); + fputs (_("\ + --backup[=CONTROL] make a backup of each existing destination file\n\ + -b like --backup but does not accept an argument\n\ + -c (ignored)\n\ + -C, --compare compare each pair of source and destination files, and\n\ + in some cases, do not modify the destination at all\n\ + -d, --directory treat all arguments as directory names; create all\n\ + components of the specified directories\n\ +"), stdout); + fputs (_("\ + -D create all leading components of DEST except the last,\n\ + then copy SOURCE to DEST\n\ + -g, --group=GROUP set group ownership, instead of process' current group\n\ + -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\ + -o, --owner=OWNER set ownership (super-user only)\n\ +"), stdout); + fputs (_("\ + -p, --preserve-timestamps apply access/modification times of SOURCE files\n\ + to corresponding destination files\n\ + -s, --strip strip symbol tables\n\ + --strip-program=PROGRAM program used to strip binaries\n\ + -S, --suffix=SUFFIX override the usual backup suffix\n\ + -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\ + -T, --no-target-directory treat DEST as a normal file\n\ + -v, --verbose print the name of each directory as it is created\n\ +"), stdout); + fputs (_("\ + --preserve-context preserve SELinux security context\n\ + -Z, --context=CONTEXT set SELinux security context of files and directories\ +\n\ +"), stdout); - if (!target_directory) - { - if (! (mkdir_and_install - ? install_file_in_file_parents (file[0], file[1], &x) - : install_file_in_file (file[0], file[1], &x))) - exit_status = EXIT_FAILURE; - } - else - { - int i; - dest_info_init (&x); - for (i = 0; i < n_files; i++) - if (! install_file_in_dir (file[i], target_directory, &x)) - exit_status = EXIT_FAILURE; - } + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\ +\n\ +The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ +The version control method may be selected via the --backup option or through\n\ +the VERSION_CONTROL environment variable. Here are the values:\n\ +\n\ +"), stdout); + fputs (_("\ + none, off never make backups (even if --backup is given)\n\ + numbered, t make numbered backups\n\ + existing, nil numbered if numbered backups exist, simple otherwise\n\ + simple, never always make simple backups\n\ +"), stdout); + emit_ancillary_info (); } + exit (status); +} - exit (exit_status); +/* Copy file FROM onto file TO and give TO the appropriate + attributes. + Return true if successful. */ + +static bool +install_file_in_file (const char *from, const char *to, + const struct cp_options *x) +{ + struct stat from_sb; + if (x->preserve_timestamps && stat (from, &from_sb) != 0) + { + error (0, errno, _("cannot stat %s"), quote (from)); + return false; + } + if (! copy_file (from, to, x)) + return false; + if (strip_files) + strip (to); + if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)) + && ! change_timestamps (&from_sb, to)) + return false; + return change_attributes (to); } /* Copy file FROM onto file TO, creating any missing parent directories of TO. @@ -721,30 +714,6 @@ install_file_in_file_parents (char const *from, char *to, return (status == EXIT_SUCCESS && install_file_in_file (from, to, x)); } -/* Copy file FROM onto file TO and give TO the appropriate - attributes. - Return true if successful. */ - -static bool -install_file_in_file (const char *from, const char *to, - const struct cp_options *x) -{ - struct stat from_sb; - if (x->preserve_timestamps && stat (from, &from_sb) != 0) - { - error (0, errno, _("cannot stat %s"), quote (from)); - return false; - } - if (! copy_file (from, to, x)) - return false; - if (strip_files) - strip (to); - if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode)) - && ! change_timestamps (&from_sb, to)) - return false; - return change_attributes (to); -} - /* Copy file FROM into directory TO_DIR, keeping its same name, and give the copy the appropriate attributes. Return true if successful. */ @@ -760,251 +729,265 @@ install_file_in_dir (const char *from, const char *to_dir, return ret; } -/* Copy file FROM onto file TO, creating TO if necessary. - Return true if successful. */ - -static bool -copy_file (const char *from, const char *to, const struct cp_options *x) +int +main (int argc, char **argv) { - bool copy_into_self; - - if (copy_only_if_needed && !need_copy (from, to, x)) - return true; - - /* Allow installing from non-regular files like /dev/null. - Charles Karney reported that some Sun version of install allows that - and that sendmail's installation process relies on the behavior. - However, since !x->recursive, the call to "copy" will fail if FROM - is a directory. */ - - return copy (from, to, false, x, ©_into_self, NULL); -} - -/* Set the attributes of file or directory NAME. - Return true if successful. */ - -static bool -change_attributes (char const *name) -{ - bool ok = false; - /* chown must precede chmod because on some systems, - chown clears the set[ug]id bits for non-superusers, - resulting in incorrect permissions. - On System V, users can give away files with chown and then not - be able to chmod them. So don't give files away. - - We don't normally ignore errors from chown because the idea of - the install command is that the file is supposed to end up with - precisely the attributes that the user specified (or defaulted). - If the file doesn't end up with the group they asked for, they'll - want to know. */ + int optc; + int exit_status = EXIT_SUCCESS; + const char *specified_mode = NULL; + bool make_backups = false; + char *backup_suffix_string; + char *version_control_string = NULL; + bool mkdir_and_install = false; + struct cp_options x; + char const *target_directory = NULL; + bool no_target_directory = false; + int n_files; + char **file; + bool strip_program_specified = false; + security_context_t scontext = NULL; + /* set iff kernel has extra selinux system calls */ + selinux_enabled = (0 < is_selinux_enabled ()); - if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1) - && lchown (name, owner_id, group_id) != 0) - error (0, errno, _("cannot change ownership of %s"), quote (name)); - else if (chmod (name, mode) != 0) - error (0, errno, _("cannot change permissions of %s"), quote (name)); - else - ok = true; + initialize_main (&argc, &argv); + set_program_name (argv[0]); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); - if (use_default_selinux_context) - setdefaultfilecon (name); + atexit (close_stdin); - return ok; -} + cp_option_init (&x); -/* Set the timestamps of file DEST to match those of SRC_SB. - Return true if successful. */ + owner_name = NULL; + group_name = NULL; + strip_files = false; + dir_arg = false; + umask (0); -static bool -change_timestamps (struct stat const *src_sb, char const *dest) -{ - struct timespec timespec[2]; - timespec[0] = get_stat_atime (src_sb); - timespec[1] = get_stat_mtime (src_sb); + /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless + we'll actually use backup_suffix_string. */ + backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - if (utimens (dest, timespec)) + while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options, + NULL)) != -1) { - error (0, errno, _("cannot set time stamps for %s"), quote (dest)); - return false; + switch (optc) + { + case 'b': + make_backups = true; + if (optarg) + version_control_string = optarg; + break; + case 'c': + break; + case 'C': + copy_only_if_needed = true; + break; + case 's': + strip_files = true; +#ifdef SIGCHLD + /* System V fork+wait does not work if SIGCHLD is ignored. */ + signal (SIGCHLD, SIG_DFL); +#endif + break; + case STRIP_PROGRAM_OPTION: + strip_program = xstrdup (optarg); + strip_program_specified = true; + break; + case 'd': + dir_arg = true; + break; + case 'D': + mkdir_and_install = true; + break; + case 'v': + x.verbose = true; + break; + case 'g': + group_name = optarg; + break; + case 'm': + specified_mode = optarg; + break; + case 'o': + owner_name = optarg; + break; + case 'p': + x.preserve_timestamps = true; + break; + case 'S': + make_backups = true; + backup_suffix_string = optarg; + break; + case 't': + if (target_directory) + error (EXIT_FAILURE, 0, + _("multiple target directories specified")); + else + { + struct stat st; + if (stat (optarg, &st) != 0) + error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg)); + if (! S_ISDIR (st.st_mode)) + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quote (optarg)); + } + target_directory = optarg; + break; + case 'T': + no_target_directory = true; + break; + + case PRESERVE_CONTEXT_OPTION_DEPRECATED: + error (0, 0, _("WARNING: --preserve_context is deprecated; " + "use --preserve-context instead")); + /* fall through */ + case PRESERVE_CONTEXT_OPTION: + if ( ! selinux_enabled) + { + error (0, 0, _("WARNING: ignoring --preserve-context; " + "this kernel is not SELinux-enabled")); + break; + } + x.preserve_security_context = true; + use_default_selinux_context = false; + break; + case 'Z': + if ( ! selinux_enabled) + { + error (0, 0, _("WARNING: ignoring --context (-Z); " + "this kernel is not SELinux-enabled")); + break; + } + scontext = optarg; + use_default_selinux_context = false; + break; + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (EXIT_FAILURE); + } } - return true; -} -/* Strip the symbol table from the file NAME. - We could dig the magic number out of the file first to - determine whether to strip it, but the header files and - magic numbers vary so much from system to system that making - it portable would be very difficult. Not worth the effort. */ + /* Check for invalid combinations of arguments. */ + if (dir_arg && strip_files) + error (EXIT_FAILURE, 0, + _("the strip option may not be used when installing a directory")); + if (dir_arg && target_directory) + error (EXIT_FAILURE, 0, + _("target directory not allowed when installing a directory")); -static void -strip (char const *name) -{ - int status; - pid_t pid = fork (); + if (x.preserve_security_context && scontext != NULL) + error (EXIT_FAILURE, 0, + _("cannot force target context to %s and preserve it"), + quote (scontext)); - switch (pid) - { - case -1: - error (EXIT_FAILURE, errno, _("fork system call failed")); - break; - case 0: /* Child. */ - execlp (strip_program, strip_program, name, NULL); - error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program); - break; - default: /* Parent. */ - if (waitpid (pid, &status, 0) < 0) - error (EXIT_FAILURE, errno, _("waiting for strip")); - else if (! WIFEXITED (status) || WEXITSTATUS (status)) - error (EXIT_FAILURE, 0, _("strip process terminated abnormally")); - break; - } -} + if (backup_suffix_string) + simple_backup_suffix = xstrdup (backup_suffix_string); -/* Initialize the user and group ownership of the files to install. */ + x.backup_type = (make_backups + ? xget_version (_("backup type"), + version_control_string) + : no_backups); -static void -get_ids (void) -{ - struct passwd *pw; - struct group *gr; + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (scontext)); - if (owner_name) + n_files = argc - optind; + file = argv + optind; + + if (n_files <= ! (dir_arg || target_directory)) { - pw = getpwnam (owner_name); - if (pw == NULL) - { - unsigned long int tmp; - if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK - || UID_T_MAX < tmp) - error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name)); - owner_id = tmp; - } + if (n_files <= 0) + error (0, 0, _("missing file operand")); else - owner_id = pw->pw_uid; - endpwent (); + error (0, 0, _("missing destination file operand after %s"), + quote (file[0])); + usage (EXIT_FAILURE); } - else - owner_id = (uid_t) -1; - if (group_name) + if (no_target_directory) { - gr = getgrnam (group_name); - if (gr == NULL) + if (target_directory) + error (EXIT_FAILURE, 0, + _("cannot combine --target-directory (-t) " + "and --no-target-directory (-T)")); + if (2 < n_files) { - unsigned long int tmp; - if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK - || GID_T_MAX < tmp) - error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name)); - group_id = tmp; + error (0, 0, _("extra operand %s"), quote (file[2])); + usage (EXIT_FAILURE); } - else - group_id = gr->gr_gid; - endgrent (); } - else - group_id = (gid_t) -1; -} + else if (! (dir_arg || target_directory)) + { + if (2 <= n_files && target_directory_operand (file[n_files - 1])) + target_directory = file[--n_files]; + else if (2 < n_files) + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quote (file[n_files - 1])); + } -/* Report that directory DIR was made, if OPTIONS requests this. */ -static void -announce_mkdir (char const *dir, void *options) -{ - struct cp_options const *x = options; - if (x->verbose) - prog_fprintf (stdout, _("creating directory %s"), quote (dir)); -} + if (specified_mode) + { + struct mode_change *change = mode_compile (specified_mode); + if (!change) + error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode)); + mode = mode_adjust (0, false, 0, change, NULL); + dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits); + free (change); + } -/* Make ancestor directory DIR, whose last file name component is - COMPONENT, with options OPTIONS. Assume the working directory is - COMPONENT's parent. */ -static int -make_ancestor (char const *dir, char const *component, void *options) -{ - int r = mkdir (component, DEFAULT_MODE); - if (r == 0) - announce_mkdir (dir, options); - return r; -} + if (strip_program_specified && !strip_files) + error (0, 0, _("WARNING: ignoring --strip-program option as -s option was " + "not specified")); -void -usage (int status) -{ - if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + if (copy_only_if_needed && x.preserve_timestamps) + { + error (0, 0, _("options --compare (-C) and --preserve-timestamps are " + "mutually exclusive")); + usage (EXIT_FAILURE); + } + + if (copy_only_if_needed && strip_files) + { + error (0, 0, _("options --compare (-C) and --strip are mutually " + "exclusive")); + usage (EXIT_FAILURE); + } + + if (copy_only_if_needed && extra_mode (mode)) + error (0, 0, _("the --compare (-C) option is ignored when you" + " specify a mode with non-permission bits")); + + get_ids (); + + if (dir_arg) + exit_status = savewd_process_files (n_files, file, process_dir, &x); else { - printf (_("\ -Usage: %s [OPTION]... [-T] SOURCE DEST\n\ - or: %s [OPTION]... SOURCE... DIRECTORY\n\ - or: %s [OPTION]... -t DIRECTORY SOURCE...\n\ - or: %s [OPTION]... -d DIRECTORY...\n\ -"), - program_name, program_name, program_name, program_name); - fputs (_("\ -\n\ -This install program copies files (often just compiled) into destination\n\ -locations you choose. If you want to download and install a ready-to-use\n\ -package on a GNU/Linux system, you should instead be using a package manager\n\ -like yum(1) or apt-get(1).\n\ -\n\ -In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\ -the existing DIRECTORY, while setting permission modes and owner/group.\n\ -In the 4th form, create all components of the given DIRECTORY(ies).\n\ -\n\ -"), stdout); - fputs (_("\ -Mandatory arguments to long options are mandatory for short options too.\n\ -"), stdout); - fputs (_("\ - --backup[=CONTROL] make a backup of each existing destination file\n\ - -b like --backup but does not accept an argument\n\ - -c (ignored)\n\ - -C, --compare compare each pair of source and destination files, and\n\ - in some cases, do not modify the destination at all\n\ - -d, --directory treat all arguments as directory names; create all\n\ - components of the specified directories\n\ -"), stdout); - fputs (_("\ - -D create all leading components of DEST except the last,\n\ - then copy SOURCE to DEST\n\ - -g, --group=GROUP set group ownership, instead of process' current group\n\ - -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\ - -o, --owner=OWNER set ownership (super-user only)\n\ -"), stdout); - fputs (_("\ - -p, --preserve-timestamps apply access/modification times of SOURCE files\n\ - to corresponding destination files\n\ - -s, --strip strip symbol tables\n\ - --strip-program=PROGRAM program used to strip binaries\n\ - -S, --suffix=SUFFIX override the usual backup suffix\n\ - -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\ - -T, --no-target-directory treat DEST as a normal file\n\ - -v, --verbose print the name of each directory as it is created\n\ -"), stdout); - fputs (_("\ - --preserve-context preserve SELinux security context\n\ - -Z, --context=CONTEXT set SELinux security context of files and directories\ -\n\ -"), stdout); + /* FIXME: it's a little gross that this initialization is + required by copy.c::copy. */ + hash_init (); - fputs (HELP_OPTION_DESCRIPTION, stdout); - fputs (VERSION_OPTION_DESCRIPTION, stdout); - fputs (_("\ -\n\ -The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ -The version control method may be selected via the --backup option or through\n\ -the VERSION_CONTROL environment variable. Here are the values:\n\ -\n\ -"), stdout); - fputs (_("\ - none, off never make backups (even if --backup is given)\n\ - numbered, t make numbered backups\n\ - existing, nil numbered if numbered backups exist, simple otherwise\n\ - simple, never always make simple backups\n\ -"), stdout); - emit_ancillary_info (); + if (!target_directory) + { + if (! (mkdir_and_install + ? install_file_in_file_parents (file[0], file[1], &x) + : install_file_in_file (file[0], file[1], &x))) + exit_status = EXIT_FAILURE; + } + else + { + int i; + dest_info_init (&x); + for (i = 0; i < n_files; i++) + if (! install_file_in_dir (file[i], target_directory, &x)) + exit_status = EXIT_FAILURE; + } } - exit (status); + + exit (exit_status); } -- 2.7.4