Removed unused files
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@11902 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -108,7 +108,6 @@ SubInclude OBOS_TOP src apps bin diffutils ;
|
|||||||
SubInclude OBOS_TOP src apps bin findutils ;
|
SubInclude OBOS_TOP src apps bin findutils ;
|
||||||
SubInclude OBOS_TOP src apps bin flex ;
|
SubInclude OBOS_TOP src apps bin flex ;
|
||||||
SubInclude OBOS_TOP src apps bin gawk ;
|
SubInclude OBOS_TOP src apps bin gawk ;
|
||||||
#SubInclude OBOS_TOP src apps bin gnu ;
|
|
||||||
SubInclude OBOS_TOP src apps bin grep ;
|
SubInclude OBOS_TOP src apps bin grep ;
|
||||||
SubInclude OBOS_TOP src apps bin keymap ;
|
SubInclude OBOS_TOP src apps bin keymap ;
|
||||||
SubInclude OBOS_TOP src apps bin less ;
|
SubInclude OBOS_TOP src apps bin less ;
|
||||||
@@ -118,7 +117,6 @@ SubInclude OBOS_TOP src apps bin makeudfimage ;
|
|||||||
SubInclude OBOS_TOP src apps bin mkdos ;
|
SubInclude OBOS_TOP src apps bin mkdos ;
|
||||||
SubInclude OBOS_TOP src apps bin patch ;
|
SubInclude OBOS_TOP src apps bin patch ;
|
||||||
SubInclude OBOS_TOP src apps bin pc ;
|
SubInclude OBOS_TOP src apps bin pc ;
|
||||||
#SubInclude OBOS_TOP src apps bin sed ;
|
|
||||||
SubInclude OBOS_TOP src apps bin sed ;
|
SubInclude OBOS_TOP src apps bin sed ;
|
||||||
SubInclude OBOS_TOP src apps bin sharutils ;
|
SubInclude OBOS_TOP src apps bin sharutils ;
|
||||||
SubInclude OBOS_TOP src apps bin strace ;
|
SubInclude OBOS_TOP src apps bin strace ;
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
basename(C) -- remove directory names from pathnames
|
|
||||||
|
|
||||||
OBOS Release 1.0 -- 1st January 2003
|
|
||||||
|
|
||||||
Additional Code by Sikosis ([email protected])
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "stdio.h"
|
|
||||||
|
|
||||||
#define USAGE "basename(C) -- remove directory names from pathnames
|
|
||||||
|
|
||||||
Syntax
|
|
||||||
|
|
||||||
basename string [ suffix ]
|
|
||||||
|
|
||||||
Description
|
|
||||||
The basename command deletes any prefix ending in ``/'' and the suffix (if present in string) from string, and prints the result on the standard output. The result is the ``base'' name of the file, that is, the filename without any preceding directory path and without an extension. It is used inside substitution marks (` `) in shell procedures to construct new filenames.
|
|
||||||
|
|
||||||
Note that the suffix is not deleted if it is identical to the resulting basename.
|
|
||||||
|
|
||||||
The related command dirname deletes the last level from string and prints the resulting path on the standard output.
|
|
||||||
Exit values
|
|
||||||
basename returns the following values:
|
|
||||||
|
|
||||||
0
|
|
||||||
successful completion
|
|
||||||
|
|
||||||
>0
|
|
||||||
an error occurred
|
|
||||||
|
|
||||||
Examples
|
|
||||||
The following command displays the filename memos on the standard output:
|
|
||||||
|
|
||||||
basename /usr/johnh/memos.old .old
|
|
||||||
|
|
||||||
The following shell procedure, when invoked with the argument /usr/src/cmd/cat.c, compiles the named file and moves the output to a file named cat in the current directory:
|
|
||||||
|
|
||||||
cc $1
|
|
||||||
mv a.out `basename $1 .c`
|
|
||||||
|
|
||||||
See also
|
|
||||||
dirname(C), sh(C)"
|
|
||||||
|
|
||||||
|
|
||||||
main(argc, argv)
|
|
||||||
char **argv;
|
|
||||||
{
|
|
||||||
register char *p1, *p2, *p3;
|
|
||||||
|
|
||||||
p1 = argv[1];
|
|
||||||
|
|
||||||
if (!strcmp(p1,"--help"))
|
|
||||||
{
|
|
||||||
putchar('\n');
|
|
||||||
puts(USAGE);
|
|
||||||
putchar('\n');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
putchar('\n');
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
p2 = p1;
|
|
||||||
while (*p1) {
|
|
||||||
if (*p1++ == '/')
|
|
||||||
p2 = p1;
|
|
||||||
}
|
|
||||||
if (argc>2) {
|
|
||||||
for(p3=argv[2]; *p3; p3++)
|
|
||||||
;
|
|
||||||
while(p1>p2 && p3>argv[2])
|
|
||||||
if(*--p3 != *--p1)
|
|
||||||
goto output;
|
|
||||||
*p1 = '\0';
|
|
||||||
}
|
|
||||||
output:
|
|
||||||
puts(p2); // original code was this:- puts(p2, stdout);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
/* chgrp -- change group ownership of files
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by David MacKenzie <[email protected]>. */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <grp.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "lchown.h"
|
|
||||||
#include "group-member.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "savedir.h"
|
|
||||||
#include "xstrtol.h"
|
|
||||||
#include "chown-core.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "chgrp"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
/* MAXUID may come from limits.h *or* sys/params.h (via system.h) above. */
|
|
||||||
#ifndef MAXUID
|
|
||||||
# define MAXUID UID_T_MAX
|
|
||||||
#endif
|
|
||||||
#ifndef MAXGID
|
|
||||||
# define MAXGID GID_T_MAX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _POSIX_VERSION
|
|
||||||
struct group *getgrnam ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ! HAVE_ENDGRENT
|
|
||||||
# define endgrent() ((void) 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The name the program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* The argument to the --reference option. Use the group ID of this file.
|
|
||||||
This file must exist. */
|
|
||||||
static char *reference_file;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
REFERENCE_FILE_OPTION = CHAR_MAX + 1,
|
|
||||||
DEREFERENCE_OPTION
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"recursive", no_argument, 0, 'R'},
|
|
||||||
{"changes", no_argument, 0, 'c'},
|
|
||||||
{"dereference", no_argument, 0, DEREFERENCE_OPTION},
|
|
||||||
{"no-dereference", no_argument, 0, 'h'},
|
|
||||||
{"quiet", no_argument, 0, 'f'},
|
|
||||||
{"silent", no_argument, 0, 'f'},
|
|
||||||
{"reference", required_argument, 0, REFERENCE_FILE_OPTION},
|
|
||||||
{"verbose", no_argument, 0, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Set *G according to NAME. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
parse_group (const char *name, gid_t *g)
|
|
||||||
{
|
|
||||||
struct group *grp;
|
|
||||||
|
|
||||||
if (*name == '\0')
|
|
||||||
error (1, 0, _("cannot change to null group"));
|
|
||||||
|
|
||||||
grp = getgrnam (name);
|
|
||||||
if (grp == NULL)
|
|
||||||
{
|
|
||||||
strtol_error s_err;
|
|
||||||
unsigned long int tmp_long;
|
|
||||||
|
|
||||||
if (!ISDIGIT (*name))
|
|
||||||
error (1, 0, _("invalid group name %s"), quote (name));
|
|
||||||
|
|
||||||
s_err = xstrtoul (name, NULL, 0, &tmp_long, NULL);
|
|
||||||
if (s_err != LONGINT_OK)
|
|
||||||
STRTOL_FATAL_ERROR (name, _("group number"), s_err);
|
|
||||||
|
|
||||||
if (tmp_long > MAXGID)
|
|
||||||
error (1, 0, _("invalid group number %s"), quote (name));
|
|
||||||
|
|
||||||
*g = tmp_long;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*g = grp->gr_gid;
|
|
||||||
endgrent (); /* Save a file descriptor. */
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... GROUP FILE...\n\
|
|
||||||
or: %s [OPTION]... --reference=RFILE FILE...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Change the group membership of each FILE to GROUP.\n\
|
|
||||||
\n\
|
|
||||||
-c, --changes like verbose but report only when a change is made\n\
|
|
||||||
--dereference affect the referent of each symbolic link, rather\n\
|
|
||||||
than the symbolic link itself\n\
|
|
||||||
-h, --no-dereference affect symbolic links instead of any referenced file\n\
|
|
||||||
(available only on systems that can change the\n\
|
|
||||||
ownership of a symlink)\n\
|
|
||||||
-f, --silent, --quiet suppress most error messages\n\
|
|
||||||
--reference=RFILE use RFILE's group rather than the specified\n\
|
|
||||||
GROUP value\n\
|
|
||||||
-R, --recursive operate on files and directories recursively\n\
|
|
||||||
-v, --verbose output a diagnostic for every file processed\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
gid_t gid;
|
|
||||||
int errors = 0;
|
|
||||||
int optc;
|
|
||||||
struct Chown_option chopt;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
chopt_init (&chopt);
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case REFERENCE_FILE_OPTION:
|
|
||||||
reference_file = optarg;
|
|
||||||
break;
|
|
||||||
case DEREFERENCE_OPTION:
|
|
||||||
chopt.dereference = DEREF_ALWAYS;
|
|
||||||
break;
|
|
||||||
case 'R':
|
|
||||||
chopt.recurse = 1;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
chopt.verbosity = V_changes_only;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
chopt.force_silent = 1;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
chopt.dereference = DEREF_NEVER;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
chopt.verbosity = V_high;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc - optind + (reference_file ? 1 : 0) <= 1)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reference_file)
|
|
||||||
{
|
|
||||||
struct stat ref_stats;
|
|
||||||
if (stat (reference_file, &ref_stats))
|
|
||||||
error (1, errno, _("getting attributes of %s"), quote (reference_file));
|
|
||||||
|
|
||||||
chopt.group_name = gid_to_name (ref_stats.st_gid);
|
|
||||||
gid = ref_stats.st_gid;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chopt.group_name = argv[optind++];
|
|
||||||
parse_group (chopt.group_name, &gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
errors |= change_file_owner (1, argv[optind], (uid_t) -1, gid,
|
|
||||||
(uid_t) -1, (gid_t) -1, &chopt);
|
|
||||||
|
|
||||||
chopt_free (&chopt);
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
/* chmod -- change permission modes of files
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "filemode.h"
|
|
||||||
#include "modechange.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "savedir.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "chmod"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
enum Change_status
|
|
||||||
{
|
|
||||||
CH_SUCCEEDED,
|
|
||||||
CH_FAILED,
|
|
||||||
CH_NO_CHANGE_REQUESTED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Verbosity
|
|
||||||
{
|
|
||||||
/* Print a message for each file that is processed. */
|
|
||||||
V_high,
|
|
||||||
|
|
||||||
/* Print a message for each file whose attributes we change. */
|
|
||||||
V_changes_only,
|
|
||||||
|
|
||||||
/* Do not be verbose. This is the default. */
|
|
||||||
V_off
|
|
||||||
};
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
static int change_dir_mode PARAMS ((const char *dir,
|
|
||||||
const struct mode_change *changes,
|
|
||||||
const struct stat *statp));
|
|
||||||
|
|
||||||
/* The name the program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, change the modes of directories recursively. */
|
|
||||||
static int recurse;
|
|
||||||
|
|
||||||
/* If nonzero, force silence (no error messages). */
|
|
||||||
static int force_silent;
|
|
||||||
|
|
||||||
/* Level of verbosity. */
|
|
||||||
static enum Verbosity verbosity = V_off;
|
|
||||||
|
|
||||||
/* The argument to the --reference option. Use the owner and group IDs
|
|
||||||
of this file. This file must exist. */
|
|
||||||
static char *reference_file;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
REFERENCE_FILE_OPTION = CHAR_MAX + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"recursive", no_argument, 0, 'R'},
|
|
||||||
{"changes", no_argument, 0, 'c'},
|
|
||||||
{"silent", no_argument, 0, 'f'},
|
|
||||||
{"quiet", no_argument, 0, 'f'},
|
|
||||||
{"reference", required_argument, 0, REFERENCE_FILE_OPTION},
|
|
||||||
{"verbose", no_argument, 0, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
mode_changed (const char *file, mode_t old_mode)
|
|
||||||
{
|
|
||||||
struct stat new_stats;
|
|
||||||
|
|
||||||
if (lstat (file, &new_stats))
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, errno, _("getting new attributes of %s"), quote (file));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
if (S_ISLNK (new_stats.st_mode)
|
|
||||||
&& stat (file, &new_stats))
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, errno, _("getting new attributes of %s"), quote (file));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return old_mode != new_stats.st_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tell the user how/if the MODE of FILE has been changed.
|
|
||||||
CHANGED describes what (if anything) has happened. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
describe_change (const char *file, mode_t mode,
|
|
||||||
enum Change_status changed)
|
|
||||||
{
|
|
||||||
char perms[11]; /* "-rwxrwxrwx" ls-style modes. */
|
|
||||||
const char *fmt;
|
|
||||||
|
|
||||||
mode_string (mode, perms);
|
|
||||||
perms[10] = '\0'; /* `mode_string' does not null terminate. */
|
|
||||||
switch (changed)
|
|
||||||
{
|
|
||||||
case CH_SUCCEEDED:
|
|
||||||
fmt = _("mode of %s changed to %04lo (%s)\n");
|
|
||||||
break;
|
|
||||||
case CH_FAILED:
|
|
||||||
fmt = _("failed to change mode of %s to %04lo (%s)\n");
|
|
||||||
break;
|
|
||||||
case CH_NO_CHANGE_REQUESTED:
|
|
||||||
fmt = _("mode of %s retained as %04lo (%s)\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
printf (fmt, quote (file),
|
|
||||||
(unsigned long) (mode & CHMOD_MODE_BITS), &perms[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Change the mode of FILE according to the list of operations CHANGES.
|
|
||||||
If DEREF_SYMLINK is nonzero and FILE is a symbolic link, change the
|
|
||||||
mode of the referenced file. If DEREF_SYMLINK is zero, ignore symbolic
|
|
||||||
links. Return 0 if successful, 1 if errors occurred. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_file_mode (const char *file, const struct mode_change *changes,
|
|
||||||
const int deref_symlink)
|
|
||||||
{
|
|
||||||
struct stat file_stats;
|
|
||||||
mode_t newmode;
|
|
||||||
int errors = 0;
|
|
||||||
int fail;
|
|
||||||
int saved_errno;
|
|
||||||
|
|
||||||
if (lstat (file, &file_stats))
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, errno, _("getting attributes of %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
if (S_ISLNK (file_stats.st_mode))
|
|
||||||
{
|
|
||||||
if (! deref_symlink)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
if (stat (file, &file_stats))
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, errno, _("getting attributes of %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
newmode = mode_adjust (file_stats.st_mode, changes);
|
|
||||||
|
|
||||||
fail = chmod (file, newmode);
|
|
||||||
saved_errno = errno;
|
|
||||||
|
|
||||||
if (verbosity == V_high
|
|
||||||
|| (verbosity == V_changes_only
|
|
||||||
&& !fail && mode_changed (file, file_stats.st_mode)))
|
|
||||||
describe_change (file, newmode, (fail ? CH_FAILED : CH_SUCCEEDED));
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, saved_errno, _("changing permissions of %s"),
|
|
||||||
quote (file));
|
|
||||||
errors = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse && S_ISDIR (file_stats.st_mode))
|
|
||||||
errors |= change_dir_mode (file, changes, &file_stats);
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively change the modes of the files in directory DIR
|
|
||||||
according to the list of operations CHANGES.
|
|
||||||
STATP points to the results of lstat on DIR.
|
|
||||||
Return 0 if successful, 1 if errors occurred. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_dir_mode (const char *dir, const struct mode_change *changes,
|
|
||||||
const struct stat *statp)
|
|
||||||
{
|
|
||||||
char *name_space, *namep;
|
|
||||||
char *path; /* Full path of each entry to process. */
|
|
||||||
unsigned dirlength; /* Length of DIR and '\0'. */
|
|
||||||
unsigned filelength; /* Length of each pathname to process. */
|
|
||||||
unsigned pathlength; /* Bytes allocated for `path'. */
|
|
||||||
int errors = 0;
|
|
||||||
|
|
||||||
name_space = savedir (dir, statp->st_size);
|
|
||||||
if (name_space == NULL)
|
|
||||||
{
|
|
||||||
if (force_silent == 0)
|
|
||||||
error (0, errno, "%s", quote (dir));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
|
|
||||||
pathlength = dirlength + 1;
|
|
||||||
/* Give `path' a dummy value; it will be reallocated before first use. */
|
|
||||||
path = xmalloc (pathlength);
|
|
||||||
strcpy (path, dir);
|
|
||||||
path[dirlength - 1] = '/';
|
|
||||||
|
|
||||||
for (namep = name_space; *namep; namep += filelength - dirlength)
|
|
||||||
{
|
|
||||||
filelength = dirlength + strlen (namep) + 1;
|
|
||||||
if (filelength > pathlength)
|
|
||||||
{
|
|
||||||
pathlength = filelength * 2;
|
|
||||||
path = xrealloc (path, pathlength);
|
|
||||||
}
|
|
||||||
strcpy (path + dirlength, namep);
|
|
||||||
errors |= change_file_mode (path, changes, 0);
|
|
||||||
}
|
|
||||||
free (path);
|
|
||||||
free (name_space);
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... MODE[,MODE]... FILE...\n\
|
|
||||||
or: %s [OPTION]... OCTAL-MODE FILE...\n\
|
|
||||||
or: %s [OPTION]... --reference=RFILE FILE...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Change the mode of each FILE to MODE.\n\
|
|
||||||
\n\
|
|
||||||
-c, --changes like verbose but report only when a change is made\n\
|
|
||||||
-f, --silent, --quiet suppress most error messages\n\
|
|
||||||
-v, --verbose output a diagnostic for every file processed\n\
|
|
||||||
--reference=RFILE use RFILE's mode instead of MODE values\n\
|
|
||||||
-R, --recursive change files and directories recursively\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n\
|
|
||||||
one or more of the letters rwxXstugo.\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse the ASCII mode given on the command line into a linked list
|
|
||||||
of `struct mode_change' and apply that to each file argument. */
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct mode_change *changes;
|
|
||||||
int errors = 0;
|
|
||||||
int modeind = 0; /* Index of the mode argument in `argv'. */
|
|
||||||
int thisind;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
recurse = force_silent = 0;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
thisind = optind ? optind : 1;
|
|
||||||
|
|
||||||
c = getopt_long (argc, argv, "RcfvrwxXstugoa,+-=", long_options, NULL);
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
case 'w':
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
case 's':
|
|
||||||
case 't':
|
|
||||||
case 'u':
|
|
||||||
case 'g':
|
|
||||||
case 'o':
|
|
||||||
case 'a':
|
|
||||||
case ',':
|
|
||||||
case '+':
|
|
||||||
case '-':
|
|
||||||
case '=':
|
|
||||||
if (modeind != 0 && modeind != thisind)
|
|
||||||
{
|
|
||||||
static char char_string[2] = {0, 0};
|
|
||||||
char_string[0] = c;
|
|
||||||
error (1, 0, _("invalid character %s in mode string %s"),
|
|
||||||
quote_n (0, char_string), quote_n (1, argv[thisind]));
|
|
||||||
}
|
|
||||||
modeind = thisind;
|
|
||||||
break;
|
|
||||||
case REFERENCE_FILE_OPTION:
|
|
||||||
reference_file = optarg;
|
|
||||||
break;
|
|
||||||
case 'R':
|
|
||||||
recurse = 1;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
verbosity = V_changes_only;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
force_silent = 1;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbosity = V_high;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modeind == 0 && reference_file == NULL)
|
|
||||||
modeind = optind++;
|
|
||||||
|
|
||||||
if (optind >= argc)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
changes = (reference_file ? mode_create_from_ref (reference_file)
|
|
||||||
: mode_compile (argv[modeind], MODE_MASK_ALL));
|
|
||||||
|
|
||||||
if (changes == MODE_INVALID)
|
|
||||||
error (1, 0, _("invalid mode string: %s"), quote (argv[modeind]));
|
|
||||||
else if (changes == MODE_MEMORY_EXHAUSTED)
|
|
||||||
xalloc_die ();
|
|
||||||
else if (changes == MODE_BAD_REFERENCE)
|
|
||||||
error (1, errno, _("getting attributes of %s"), quote (reference_file));
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
{
|
|
||||||
strip_trailing_slashes (argv[optind]);
|
|
||||||
errors |= change_file_mode (argv[optind], changes, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
/* chown-core.c -- core functions for changing ownership.
|
|
||||||
Copyright (C) 2000 Free Software Foundation.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Extracted from chown.c/chgrp.c and librarified by Jim Meyering. */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <grp.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "lchown.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "savedir.h"
|
|
||||||
#include "chown-core.h"
|
|
||||||
#include "xalloc.h"
|
|
||||||
|
|
||||||
/* The number of decimal digits required to represent the largest value of
|
|
||||||
type `unsigned int'. This is enough for an 8-byte unsigned int type. */
|
|
||||||
#define UINT_MAX_DECIMAL_DIGITS 20
|
|
||||||
|
|
||||||
#ifndef _POSIX_VERSION
|
|
||||||
struct group *getgrnam ();
|
|
||||||
struct group *getgrgid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int lstat ();
|
|
||||||
|
|
||||||
void
|
|
||||||
chopt_init (struct Chown_option *chopt)
|
|
||||||
{
|
|
||||||
chopt->verbosity = V_off;
|
|
||||||
chopt->dereference = DEREF_NEVER;
|
|
||||||
chopt->recurse = 0;
|
|
||||||
chopt->force_silent = 0;
|
|
||||||
chopt->user_name = 0;
|
|
||||||
chopt->group_name = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
chopt_free (struct Chown_option *chopt)
|
|
||||||
{
|
|
||||||
/* Deliberately do not free chopt->user_name or ->group_name.
|
|
||||||
They're not always allocated. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert N to a string, and return a pointer to that string in memory
|
|
||||||
allocated from the heap. */
|
|
||||||
|
|
||||||
static char *
|
|
||||||
uint_to_string (unsigned int n)
|
|
||||||
{
|
|
||||||
char buf[UINT_MAX_DECIMAL_DIGITS + 1];
|
|
||||||
char *p = buf + sizeof buf;
|
|
||||||
|
|
||||||
*--p = '\0';
|
|
||||||
|
|
||||||
do
|
|
||||||
*--p = '0' + (n % 10);
|
|
||||||
while ((n /= 10) != 0);
|
|
||||||
|
|
||||||
return xstrdup (p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert the numeric group-id, GID, to a string stored in xmalloc'd memory,
|
|
||||||
and return it. If there's no corresponding group name, use the decimal
|
|
||||||
representation of the ID. */
|
|
||||||
|
|
||||||
char *
|
|
||||||
gid_to_name (gid_t gid)
|
|
||||||
{
|
|
||||||
struct group *grp = getgrgid (gid);
|
|
||||||
return grp ? xstrdup (grp->gr_name) : uint_to_string (gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert the numeric user-id, UID, to a string stored in xmalloc'd memory,
|
|
||||||
and return it. If there's no corresponding user name, use the decimal
|
|
||||||
representation of the ID. */
|
|
||||||
|
|
||||||
char *
|
|
||||||
uid_to_name (uid_t uid)
|
|
||||||
{
|
|
||||||
struct passwd *pwd = getpwuid (uid);
|
|
||||||
return pwd ? xstrdup (pwd->pw_name) : uint_to_string (uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tell the user how/if the user and group of FILE have been changed.
|
|
||||||
If USER is NULL, give the group-oriented messages.
|
|
||||||
CHANGED describes what (if anything) has happened. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
describe_change (const char *file, enum Change_status changed,
|
|
||||||
char const *user, char const *group)
|
|
||||||
{
|
|
||||||
const char *fmt;
|
|
||||||
char *spec;
|
|
||||||
int spec_allocated = 0;
|
|
||||||
|
|
||||||
if (changed == CH_NOT_APPLIED)
|
|
||||||
{
|
|
||||||
printf (_("neither symbolic link %s nor referent has been changed\n"),
|
|
||||||
quote (file));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user)
|
|
||||||
{
|
|
||||||
if (group)
|
|
||||||
{
|
|
||||||
spec = xmalloc (strlen (user) + 1 + strlen (group) + 1);
|
|
||||||
stpcpy (stpcpy (stpcpy (spec, user), ":"), group);
|
|
||||||
spec_allocated = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spec = (char *) user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spec = (char *) group;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (changed)
|
|
||||||
{
|
|
||||||
case CH_SUCCEEDED:
|
|
||||||
fmt = (user
|
|
||||||
? _("changed ownership of %s to %s\n")
|
|
||||||
: _("changed group of %s to %s\n"));
|
|
||||||
break;
|
|
||||||
case CH_FAILED:
|
|
||||||
fmt = (user
|
|
||||||
? _("failed to change ownership of %s to %s\n")
|
|
||||||
: _("failed to change group of %s to %s\n"));
|
|
||||||
break;
|
|
||||||
case CH_NO_CHANGE_REQUESTED:
|
|
||||||
fmt = (user
|
|
||||||
? _("ownership of %s retained as %s\n")
|
|
||||||
: _("group of %s retained as %s\n"));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
printf (fmt, quote (file), spec);
|
|
||||||
|
|
||||||
if (spec_allocated)
|
|
||||||
free (spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively change the ownership of the files in directory DIR to user-id,
|
|
||||||
UID, and group-id, GID, according to the options specified by CHOPT.
|
|
||||||
STATP points to the results of lstat on DIR.
|
|
||||||
Return 0 if successful, 1 if errors occurred. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_dir_owner (const char *dir, uid_t uid, gid_t gid,
|
|
||||||
uid_t old_uid, gid_t old_gid,
|
|
||||||
const struct stat *statp,
|
|
||||||
struct Chown_option const *chopt)
|
|
||||||
{
|
|
||||||
char *name_space, *namep;
|
|
||||||
char *path; /* Full path of each entry to process. */
|
|
||||||
unsigned dirlength; /* Length of `dir' and '\0'. */
|
|
||||||
unsigned filelength; /* Length of each pathname to process. */
|
|
||||||
unsigned pathlength; /* Bytes allocated for `path'. */
|
|
||||||
int errors = 0;
|
|
||||||
|
|
||||||
name_space = savedir (dir, statp->st_size);
|
|
||||||
if (name_space == NULL)
|
|
||||||
{
|
|
||||||
if (chopt->force_silent == 0)
|
|
||||||
error (0, errno, "%s", quote (dir));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
|
|
||||||
pathlength = dirlength + 1;
|
|
||||||
/* Give `path' a dummy value; it will be reallocated before first use. */
|
|
||||||
path = xmalloc (pathlength);
|
|
||||||
strcpy (path, dir);
|
|
||||||
path[dirlength - 1] = '/';
|
|
||||||
|
|
||||||
for (namep = name_space; *namep; namep += filelength - dirlength)
|
|
||||||
{
|
|
||||||
filelength = dirlength + strlen (namep) + 1;
|
|
||||||
if (filelength > pathlength)
|
|
||||||
{
|
|
||||||
pathlength = filelength * 2;
|
|
||||||
path = xrealloc (path, pathlength);
|
|
||||||
}
|
|
||||||
strcpy (path + dirlength, namep);
|
|
||||||
errors |= change_file_owner (0, path, uid, gid, old_uid, old_gid,
|
|
||||||
chopt);
|
|
||||||
}
|
|
||||||
free (path);
|
|
||||||
free (name_space);
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Change the ownership of FILE to user-id, UID, and group-id, GID,
|
|
||||||
provided it presently has owner OLD_UID and group OLD_GID.
|
|
||||||
Honor the options specified by CHOPT.
|
|
||||||
If FILE is a directory and -R is given, recurse.
|
|
||||||
Return 0 if successful, 1 if errors occurred. */
|
|
||||||
|
|
||||||
int
|
|
||||||
change_file_owner (int cmdline_arg, const char *file, uid_t uid, gid_t gid,
|
|
||||||
uid_t old_uid, gid_t old_gid,
|
|
||||||
struct Chown_option const *chopt)
|
|
||||||
{
|
|
||||||
struct stat file_stats;
|
|
||||||
uid_t new_uid;
|
|
||||||
gid_t new_gid;
|
|
||||||
int errors = 0;
|
|
||||||
int is_symlink;
|
|
||||||
int is_directory;
|
|
||||||
|
|
||||||
if (lstat (file, &file_stats))
|
|
||||||
{
|
|
||||||
if (chopt->force_silent == 0)
|
|
||||||
error (0, errno, _("getting attributes of %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it's a symlink and we're dereferencing, then use stat
|
|
||||||
to get the attributes of the referent. */
|
|
||||||
if (S_ISLNK (file_stats.st_mode))
|
|
||||||
{
|
|
||||||
if (chopt->dereference == DEREF_ALWAYS
|
|
||||||
&& stat (file, &file_stats))
|
|
||||||
{
|
|
||||||
if (chopt->force_silent == 0)
|
|
||||||
error (0, errno, _("getting attributes of %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_symlink = 1;
|
|
||||||
|
|
||||||
/* With -R, don't traverse through symlinks-to-directories.
|
|
||||||
But of course, this will all change with POSIX's new
|
|
||||||
-H, -L, -P options. */
|
|
||||||
is_directory = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
is_symlink = 0;
|
|
||||||
is_directory = S_ISDIR (file_stats.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((old_uid == (uid_t) -1 || file_stats.st_uid == old_uid)
|
|
||||||
&& (old_gid == (gid_t) -1 || file_stats.st_gid == old_gid))
|
|
||||||
{
|
|
||||||
new_uid = (uid == (uid_t) -1 ? file_stats.st_uid : uid);
|
|
||||||
new_gid = (gid == (gid_t) -1 ? file_stats.st_gid : gid);
|
|
||||||
if (new_uid != file_stats.st_uid || new_gid != file_stats.st_gid)
|
|
||||||
{
|
|
||||||
int fail;
|
|
||||||
int symlink_changed = 1;
|
|
||||||
int saved_errno;
|
|
||||||
int called_lchown = 0;
|
|
||||||
|
|
||||||
if (is_symlink)
|
|
||||||
{
|
|
||||||
if (chopt->dereference == DEREF_NEVER)
|
|
||||||
{
|
|
||||||
called_lchown = 1;
|
|
||||||
fail = lchown (file, new_uid, new_gid);
|
|
||||||
|
|
||||||
/* Ignore the failure if it's due to lack of support (ENOSYS)
|
|
||||||
and this is not a command line argument. */
|
|
||||||
if (!cmdline_arg && fail && errno == ENOSYS)
|
|
||||||
{
|
|
||||||
fail = 0;
|
|
||||||
symlink_changed = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (chopt->dereference == DEREF_ALWAYS)
|
|
||||||
{
|
|
||||||
/* Applying chown to a symlink and expecting it to affect
|
|
||||||
the referent is not portable. So instead, open the
|
|
||||||
file and use fchown on the resulting descriptor. */
|
|
||||||
int fd = open (file, O_RDONLY | O_NONBLOCK | O_NOCTTY);
|
|
||||||
fail = (fd == -1 ? 1 : fchown (fd, new_uid, new_gid));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* FIXME */
|
|
||||||
abort ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fail = chown (file, new_uid, new_gid);
|
|
||||||
}
|
|
||||||
saved_errno = errno;
|
|
||||||
|
|
||||||
if (chopt->verbosity == V_high
|
|
||||||
|| (chopt->verbosity == V_changes_only && !fail))
|
|
||||||
{
|
|
||||||
enum Change_status ch_status = (! symlink_changed
|
|
||||||
? CH_NOT_APPLIED
|
|
||||||
: (fail
|
|
||||||
? CH_FAILED : CH_SUCCEEDED));
|
|
||||||
describe_change (file, ch_status,
|
|
||||||
chopt->user_name, chopt->group_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
if (chopt->force_silent == 0)
|
|
||||||
error (0, saved_errno, (uid != (uid_t) -1
|
|
||||||
? _("changing ownership of %s")
|
|
||||||
: _("changing group of %s")),
|
|
||||||
quote (file));
|
|
||||||
errors = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The change succeeded. On some systems, the chown function
|
|
||||||
resets the `special' permission bits. When run by a
|
|
||||||
`privileged' user, this program must ensure that at least
|
|
||||||
the set-uid and set-group ones are still set. */
|
|
||||||
if (file_stats.st_mode & ~(S_IFMT | S_IRWXUGO)
|
|
||||||
/* If we called lchown above (which means this is a symlink),
|
|
||||||
then skip it. */
|
|
||||||
&& ! called_lchown)
|
|
||||||
{
|
|
||||||
if (chmod (file, file_stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, saved_errno,
|
|
||||||
_("unable to restore permissions of %s"),
|
|
||||||
quote (file));
|
|
||||||
fail = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (chopt->verbosity == V_high)
|
|
||||||
{
|
|
||||||
describe_change (file, CH_NO_CHANGE_REQUESTED,
|
|
||||||
chopt->user_name, chopt->group_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chopt->recurse && is_directory)
|
|
||||||
errors |= change_dir_owner (file, uid, gid,
|
|
||||||
old_uid, old_gid, &file_stats, chopt);
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/* chown-core.h -- types and prototypes shared by chown and chgrp.
|
|
||||||
Copyright (C) 2000 Free Software Foundation.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
#ifndef CHOWN_CORE_H
|
|
||||||
# define CHOWN_CORE_H
|
|
||||||
|
|
||||||
enum Change_status
|
|
||||||
{
|
|
||||||
CH_NOT_APPLIED = 1,
|
|
||||||
CH_SUCCEEDED,
|
|
||||||
CH_FAILED,
|
|
||||||
CH_NO_CHANGE_REQUESTED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Verbosity
|
|
||||||
{
|
|
||||||
/* Print a message for each file that is processed. */
|
|
||||||
V_high,
|
|
||||||
|
|
||||||
/* Print a message for each file whose attributes we change. */
|
|
||||||
V_changes_only,
|
|
||||||
|
|
||||||
/* Do not be verbose. This is the default. */
|
|
||||||
V_off
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Dereference_symlink
|
|
||||||
{
|
|
||||||
DEREF_UNDEFINED = 1,
|
|
||||||
DEREF_NEVER, /* -P */
|
|
||||||
DEREF_COMMAND_LINE_ARGUMENTS, /* -H */
|
|
||||||
DEREF_ALWAYS /* -L */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Chown_option
|
|
||||||
{
|
|
||||||
/* Level of verbosity. */
|
|
||||||
enum Verbosity verbosity;
|
|
||||||
|
|
||||||
/* If nonzero, change the ownership of directories recursively. */
|
|
||||||
int recurse;
|
|
||||||
|
|
||||||
/* This is useful only on systems with support for changing the
|
|
||||||
ownership of symbolic links. */
|
|
||||||
enum Dereference_symlink dereference;
|
|
||||||
|
|
||||||
/* If nonzero, force silence (no error messages). */
|
|
||||||
int force_silent;
|
|
||||||
|
|
||||||
/* The name of the user to which ownership of the files is being given. */
|
|
||||||
char *user_name;
|
|
||||||
|
|
||||||
/* The name of the group to which ownership of the files is being given. */
|
|
||||||
char *group_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
chopt_init (struct Chown_option *);
|
|
||||||
|
|
||||||
void
|
|
||||||
chopt_free (struct Chown_option *);
|
|
||||||
|
|
||||||
char *
|
|
||||||
gid_to_name (gid_t);
|
|
||||||
|
|
||||||
char *
|
|
||||||
uid_to_name (uid_t);
|
|
||||||
|
|
||||||
int
|
|
||||||
change_file_owner PARAMS ((int, const char *,
|
|
||||||
uid_t, gid_t,
|
|
||||||
uid_t, gid_t,
|
|
||||||
struct Chown_option const *));
|
|
||||||
|
|
||||||
#endif /* CHOWN_CORE_H */
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
/* chown -- change user and group ownership of files
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
| user
|
|
||||||
| unchanged explicit
|
|
||||||
-------------|-------------------------+-------------------------|
|
|
||||||
g unchanged | --- | chown u |
|
|
||||||
r |-------------------------+-------------------------|
|
|
||||||
o explicit | chgrp g or chown .g | chown u.g |
|
|
||||||
u |-------------------------+-------------------------|
|
|
||||||
p from passwd| --- | chown u. |
|
|
||||||
|-------------------------+-------------------------|
|
|
||||||
|
|
||||||
Written by David MacKenzie <[email protected]>. */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "lchown.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "savedir.h"
|
|
||||||
#include "chown-core.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "chown"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
#ifndef _POSIX_VERSION
|
|
||||||
struct passwd *getpwnam ();
|
|
||||||
struct group *getgrnam ();
|
|
||||||
struct group *getgrgid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ! HAVE_ENDPWENT
|
|
||||||
# define endpwent() ((void) 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *parse_user_spec ();
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* The name the program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* The argument to the --reference option. Use the owner and group IDs
|
|
||||||
of this file. This file must exist. */
|
|
||||||
static char *reference_file;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
REFERENCE_FILE_OPTION = CHAR_MAX + 1,
|
|
||||||
DEREFERENCE_OPTION,
|
|
||||||
FROM_OPTION
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"recursive", no_argument, 0, 'R'},
|
|
||||||
{"changes", no_argument, 0, 'c'},
|
|
||||||
{"dereference", no_argument, 0, DEREFERENCE_OPTION},
|
|
||||||
{"from", required_argument, 0, FROM_OPTION},
|
|
||||||
{"no-dereference", no_argument, 0, 'h'},
|
|
||||||
{"quiet", no_argument, 0, 'f'},
|
|
||||||
{"silent", no_argument, 0, 'f'},
|
|
||||||
{"reference", required_argument, 0, REFERENCE_FILE_OPTION},
|
|
||||||
{"verbose", no_argument, 0, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... OWNER[:[GROUP]] FILE...\n\
|
|
||||||
or: %s [OPTION]... :GROUP FILE...\n\
|
|
||||||
or: %s [OPTION]... --reference=RFILE FILE...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\
|
|
||||||
\n\
|
|
||||||
-c, --changes like verbose but report only when a change is made\n\
|
|
||||||
--dereference affect the referent of each symbolic link, rather\n\
|
|
||||||
than the symbolic link itself\n\
|
|
||||||
-h, --no-dereference affect symbolic links instead of any referenced file\n\
|
|
||||||
(available only on systems that can change the\n\
|
|
||||||
ownership of a symlink)\n\
|
|
||||||
--from=CURRENT_OWNER:CURRENT_GROUP\n\
|
|
||||||
change the owner and/or group of each file only if\n\
|
|
||||||
its current owner and/or group match those specified\n\
|
|
||||||
here. Either may be omitted, in which case a match\n\
|
|
||||||
is not required for the omitted attribute.\n\
|
|
||||||
-f, --silent, --quiet suppress most error messages\n\
|
|
||||||
--reference=RFILE use RFILE's owner and group rather than\n\
|
|
||||||
the specified OWNER:GROUP values\n\
|
|
||||||
-R, --recursive operate on files and directories recursively\n\
|
|
||||||
-v, --verbose output a diagnostic for every file processed\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
Owner is unchanged if missing. Group is unchanged if missing, but changed\n\
|
|
||||||
to login group if implied by a `:'. OWNER and GROUP may be numeric as well\n\
|
|
||||||
as symbolic.\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
uid_t uid = (uid_t) -1; /* New uid; -1 if not to be changed. */
|
|
||||||
gid_t gid = (uid_t) -1; /* New gid; -1 if not to be changed. */
|
|
||||||
uid_t old_uid = (uid_t) -1; /* Old uid; -1 if unrestricted. */
|
|
||||||
gid_t old_gid = (uid_t) -1; /* Old gid; -1 if unrestricted. */
|
|
||||||
struct Chown_option chopt;
|
|
||||||
|
|
||||||
int errors = 0;
|
|
||||||
int optc;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
chopt_init (&chopt);
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case REFERENCE_FILE_OPTION:
|
|
||||||
reference_file = optarg;
|
|
||||||
break;
|
|
||||||
case DEREFERENCE_OPTION:
|
|
||||||
chopt.dereference = DEREF_ALWAYS;
|
|
||||||
break;
|
|
||||||
case FROM_OPTION:
|
|
||||||
{
|
|
||||||
char *u_dummy, *g_dummy;
|
|
||||||
const char *e = parse_user_spec (argv[optind],
|
|
||||||
&old_uid, &old_gid,
|
|
||||||
&u_dummy, &g_dummy);
|
|
||||||
if (e)
|
|
||||||
error (1, 0, "%s: %s", quote (argv[optind]), e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'R':
|
|
||||||
chopt.recurse = 1;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
chopt.verbosity = V_changes_only;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
chopt.force_silent = 1;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
chopt.dereference = DEREF_NEVER;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
chopt.verbosity = V_high;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc - optind + (reference_file ? 1 : 0) <= 1)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reference_file)
|
|
||||||
{
|
|
||||||
struct stat ref_stats;
|
|
||||||
|
|
||||||
if (stat (reference_file, &ref_stats))
|
|
||||||
error (1, errno, _("getting attributes of %s"), quote (reference_file));
|
|
||||||
|
|
||||||
uid = ref_stats.st_uid;
|
|
||||||
gid = ref_stats.st_gid;
|
|
||||||
chopt.user_name = uid_to_name (ref_stats.st_uid);
|
|
||||||
chopt.group_name = gid_to_name (ref_stats.st_gid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char *e = parse_user_spec (argv[optind], &uid, &gid,
|
|
||||||
&chopt.user_name, &chopt.group_name);
|
|
||||||
if (e)
|
|
||||||
error (1, 0, "%s: %s", quote (argv[optind]), e);
|
|
||||||
|
|
||||||
/* FIXME: set it to the empty string? */
|
|
||||||
if (chopt.user_name == NULL)
|
|
||||||
chopt.user_name = "";
|
|
||||||
|
|
||||||
optind++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
{
|
|
||||||
strip_trailing_slashes (argv[optind]);
|
|
||||||
errors |= change_file_owner (1, argv[optind], uid, gid,
|
|
||||||
old_uid, old_gid, &chopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
chopt_free (&chopt);
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,161 +0,0 @@
|
|||||||
#ifndef COPY_H
|
|
||||||
# define COPY_H
|
|
||||||
|
|
||||||
/* Control creation of sparse files (files with holes). */
|
|
||||||
enum Sparse_type
|
|
||||||
{
|
|
||||||
SPARSE_UNUSED,
|
|
||||||
|
|
||||||
/* Never create holes in DEST. */
|
|
||||||
SPARSE_NEVER,
|
|
||||||
|
|
||||||
/* This is the default. Use a crude (and sometimes inaccurate)
|
|
||||||
heuristic to determine if SOURCE has holes. If so, try to create
|
|
||||||
holes in DEST. */
|
|
||||||
SPARSE_AUTO,
|
|
||||||
|
|
||||||
/* For every sufficiently long sequence of bytes in SOURCE, try to
|
|
||||||
create a corresponding hole in DEST. There is a performance penalty
|
|
||||||
here because CP has to search for holes in SRC. But if the holes are
|
|
||||||
big enough, that penalty can be offset by the decrease in the amount
|
|
||||||
of data written to disk. */
|
|
||||||
SPARSE_ALWAYS
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Dereference_symlink
|
|
||||||
{
|
|
||||||
DEREF_UNDEFINED = 1,
|
|
||||||
DEREF_ALWAYS,
|
|
||||||
DEREF_NEVER,
|
|
||||||
DEREF_COMMAND_LINE_ARGUMENTS
|
|
||||||
};
|
|
||||||
|
|
||||||
# define VALID_SPARSE_MODE(Mode) \
|
|
||||||
((Mode) == SPARSE_NEVER \
|
|
||||||
|| (Mode) == SPARSE_AUTO \
|
|
||||||
|| (Mode) == SPARSE_ALWAYS)
|
|
||||||
|
|
||||||
struct cp_options
|
|
||||||
{
|
|
||||||
enum backup_type backup_type;
|
|
||||||
|
|
||||||
/* If nonzero, copy all files except (directories and, if not dereferencing
|
|
||||||
them, symbolic links,) as if they were regular files. */
|
|
||||||
int copy_as_regular;
|
|
||||||
|
|
||||||
/* If nonzero, dereference symbolic links (copy the files they point to). */
|
|
||||||
enum Dereference_symlink dereference;
|
|
||||||
|
|
||||||
/* If nonzero, remove each existing destination nondirectory before
|
|
||||||
trying to open it. */
|
|
||||||
int unlink_dest_before_opening;
|
|
||||||
|
|
||||||
/* If nonzero, first try to open each existing destination nondirectory,
|
|
||||||
then, if the open fails, unlink and try again.
|
|
||||||
This option must be set for `cp -f', in case the destination file
|
|
||||||
exists when the open is attempted. It is irrelevant to `mv' since
|
|
||||||
any destination is sure to be removed before the open. */
|
|
||||||
int unlink_dest_after_failed_open;
|
|
||||||
|
|
||||||
/* Setting this member is meaningful only if FORCE is also set.
|
|
||||||
If nonzero, copy returns nonzero upon failed unlink.
|
|
||||||
Otherwise, the failure still elicits a diagnostic, but it doesn't
|
|
||||||
change copy's return value. This is nonzero for cp and mv, and zero
|
|
||||||
for install. */
|
|
||||||
/* FIXME: this is now unused. */
|
|
||||||
int failed_unlink_is_fatal;
|
|
||||||
|
|
||||||
/* If nonzero, create hard links instead of copying files.
|
|
||||||
Create destination directories as usual. */
|
|
||||||
int hard_link;
|
|
||||||
|
|
||||||
/* If nonzero, query before overwriting existing destinations
|
|
||||||
with regular files. */
|
|
||||||
int interactive;
|
|
||||||
|
|
||||||
/* If nonzero, rather than copying, first attempt to use rename.
|
|
||||||
If that fails, then resort to copying. */
|
|
||||||
int move_mode;
|
|
||||||
|
|
||||||
/* This process's effective user ID. */
|
|
||||||
uid_t myeuid;
|
|
||||||
|
|
||||||
/* If nonzero, when copying recursively, skip any subdirectories that are
|
|
||||||
on different filesystems from the one we started on. */
|
|
||||||
int one_file_system;
|
|
||||||
|
|
||||||
/* If nonzero, attempt to give the copies the original files' permissions,
|
|
||||||
owner, group, and timestamps. */
|
|
||||||
int preserve_owner_and_group;
|
|
||||||
int preserve_chmod_bits;
|
|
||||||
int preserve_timestamps;
|
|
||||||
|
|
||||||
/* If nonzero and any of the above (for preserve) file attributes cannot
|
|
||||||
be applied to a destination file, treat it as a failure and return
|
|
||||||
nonzero immediately. E.g. cp -p requires this be nonzero, mv requires
|
|
||||||
it be zero. */
|
|
||||||
int require_preserve;
|
|
||||||
|
|
||||||
/* If nonzero, copy directories recursively and copy special files
|
|
||||||
as themselves rather than copying their contents. */
|
|
||||||
int recursive;
|
|
||||||
|
|
||||||
/* If nonzero, set file mode to value of MODE. Otherwise,
|
|
||||||
set it based on current umask modified by UMASK_KILL. */
|
|
||||||
int set_mode;
|
|
||||||
|
|
||||||
/* Set the mode of the destination file to exactly this value
|
|
||||||
if USE_MODE is nonzero. */
|
|
||||||
mode_t mode;
|
|
||||||
|
|
||||||
/* Control creation of sparse files. */
|
|
||||||
enum Sparse_type sparse_mode;
|
|
||||||
|
|
||||||
/* If nonzero, create symbolic links instead of copying files.
|
|
||||||
Create destination directories as usual. */
|
|
||||||
int symbolic_link;
|
|
||||||
|
|
||||||
/* The bits to preserve in created files' modes. */
|
|
||||||
mode_t umask_kill;
|
|
||||||
|
|
||||||
/* If nonzero, do not copy a nondirectory that has an existing destination
|
|
||||||
with the same or newer modification time. */
|
|
||||||
int update;
|
|
||||||
|
|
||||||
/* If nonzero, display the names of the files before copying them. */
|
|
||||||
int verbose;
|
|
||||||
|
|
||||||
/* A pointer to either lstat or stat, depending on
|
|
||||||
whether the copy should dereference symlinks. */
|
|
||||||
int (*xstat) ();
|
|
||||||
};
|
|
||||||
|
|
||||||
int stat ();
|
|
||||||
int lstat ();
|
|
||||||
|
|
||||||
/* Arrange to make lstat calls go through the wrapper function
|
|
||||||
on systems with an lstat function that does not dereference symlinks
|
|
||||||
that are specified with a trailing slash. */
|
|
||||||
# if ! LSTAT_FOLLOWS_SLASHED_SYMLINK
|
|
||||||
int rpl_lstat PARAMS((const char *, struct stat *));
|
|
||||||
# undef lstat
|
|
||||||
# define lstat rpl_lstat
|
|
||||||
# endif
|
|
||||||
|
|
||||||
int rename ();
|
|
||||||
|
|
||||||
/* Arrange to make rename calls go through the wrapper function
|
|
||||||
on systems with a rename function that fails for a source path
|
|
||||||
specified with a trailing slash. */
|
|
||||||
# if RENAME_TRAILING_SLASH_BUG
|
|
||||||
int rpl_rename PARAMS((const char *, const char *));
|
|
||||||
# undef rename
|
|
||||||
# define rename rpl_rename
|
|
||||||
# endif
|
|
||||||
|
|
||||||
int
|
|
||||||
copy PARAMS ((const char *src_path, const char *dst_path,
|
|
||||||
int nonexistent_dst, const struct cp_options *options,
|
|
||||||
int *copy_into_self, int *rename_succeeded));
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
/* cp-hash.c -- file copying (hash search routines)
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2001 Free Software Foundation.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
Written by Torbjorn Granlund, Sweden ([email protected]). */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#if HAVE_INTTYPES_H
|
|
||||||
# include <inttypes.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "cp-hash.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
struct entry
|
|
||||||
{
|
|
||||||
ino_t ino;
|
|
||||||
dev_t dev;
|
|
||||||
/* Destination path name (of non-directory or pre-existing directory)
|
|
||||||
corresponding to the dev/ino of a copied file, or the destination path
|
|
||||||
name corresponding to a dev/ino pair for a newly-created directory. */
|
|
||||||
char *node;
|
|
||||||
|
|
||||||
struct entry *coll_link; /* 0 = entry not occupied. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct htab
|
|
||||||
{
|
|
||||||
unsigned modulus; /* Size of the `hash' pointer vector. */
|
|
||||||
struct entry *entry_tab; /* Pointer to dynamically growing vector. */
|
|
||||||
unsigned entry_tab_size; /* Size of current `entry_tab' allocation. */
|
|
||||||
unsigned first_free_entry; /* Index in `entry_tab'. */
|
|
||||||
struct entry *hash[1]; /* Vector of pointers in `entry_tab'. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct htab *htab;
|
|
||||||
|
|
||||||
static char *cph_hash_insert PARAMS ((ino_t ino, dev_t dev, const char *node));
|
|
||||||
|
|
||||||
/* Add PATH to the list of files that we have created.
|
|
||||||
Return 0 if successful, 1 if not. */
|
|
||||||
|
|
||||||
int
|
|
||||||
remember_created (const char *path)
|
|
||||||
{
|
|
||||||
struct stat sb;
|
|
||||||
|
|
||||||
if (stat (path, &sb) < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cph_hash_insert (sb.st_ino, sb.st_dev, path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add path NODE, copied from inode number INO and device number DEV,
|
|
||||||
to the list of files we have copied.
|
|
||||||
Return NULL if inserted, otherwise non-NULL. */
|
|
||||||
|
|
||||||
char *
|
|
||||||
remember_copied (const char *node, ino_t ino, dev_t dev)
|
|
||||||
{
|
|
||||||
return cph_hash_insert (ino, dev, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate space for the hash structures, and set the global
|
|
||||||
variable `htab' to point to it. The initial hash module is specified in
|
|
||||||
MODULUS, and the number of entries are specified in ENTRY_TAB_SIZE. (The
|
|
||||||
hash structure will be rebuilt when ENTRY_TAB_SIZE entries have been
|
|
||||||
inserted, and MODULUS and ENTRY_TAB_SIZE in the global `htab' will be
|
|
||||||
doubled.) */
|
|
||||||
|
|
||||||
void
|
|
||||||
hash_init (unsigned int modulus, unsigned int entry_tab_size)
|
|
||||||
{
|
|
||||||
struct htab *htab_r;
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xmalloc (sizeof (struct htab) + sizeof (struct entry *) * modulus);
|
|
||||||
|
|
||||||
htab_r->entry_tab = (struct entry *)
|
|
||||||
xmalloc (sizeof (struct entry) * entry_tab_size);
|
|
||||||
|
|
||||||
htab_r->modulus = modulus;
|
|
||||||
htab_r->entry_tab_size = entry_tab_size;
|
|
||||||
htab = htab_r;
|
|
||||||
|
|
||||||
forget_all ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset the hash structure in the global variable `htab' to
|
|
||||||
contain no entries. */
|
|
||||||
|
|
||||||
void
|
|
||||||
forget_all (void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct entry **p;
|
|
||||||
|
|
||||||
htab->first_free_entry = 0;
|
|
||||||
|
|
||||||
p = htab->hash;
|
|
||||||
for (i = htab->modulus; i > 0; i--)
|
|
||||||
*p++ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert path NODE, copied from inode number INO and device number DEV,
|
|
||||||
into the hash structure HTAB, if not already present.
|
|
||||||
Return NULL if inserted, otherwise non-NULL. */
|
|
||||||
|
|
||||||
static char *
|
|
||||||
hash_insert2 (struct htab *ht, ino_t ino, dev_t dev, const char *node)
|
|
||||||
{
|
|
||||||
struct entry **hp, *ep2, *ep;
|
|
||||||
|
|
||||||
/* The cast to uintmax_t prevents negative remainders if ino is negative. */
|
|
||||||
hp = &ht->hash[(uintmax_t) ino % ht->modulus];
|
|
||||||
|
|
||||||
ep2 = *hp;
|
|
||||||
|
|
||||||
/* Collision? */
|
|
||||||
|
|
||||||
if (ep2 != NULL)
|
|
||||||
{
|
|
||||||
ep = ep2;
|
|
||||||
|
|
||||||
/* Search for an entry with the same data. */
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (ep->ino == ino && ep->dev == dev)
|
|
||||||
return ep->node; /* Found an entry with the same data. */
|
|
||||||
ep = ep->coll_link;
|
|
||||||
}
|
|
||||||
while (ep != NULL);
|
|
||||||
|
|
||||||
/* Did not find it. */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ep = *hp = &ht->entry_tab[ht->first_free_entry++];
|
|
||||||
ep->ino = ino;
|
|
||||||
ep->dev = dev;
|
|
||||||
ep->node = (char *) node;
|
|
||||||
ep->coll_link = ep2; /* ep2 is NULL if not collision. */
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert path NODE, copied from inode number INO and device number DEV,
|
|
||||||
into the hash structure in the global variable `htab', if an entry with
|
|
||||||
the same inode and device was not found already.
|
|
||||||
Return NULL if inserted, otherwise non-NULL. */
|
|
||||||
|
|
||||||
static char *
|
|
||||||
cph_hash_insert (ino_t ino, dev_t dev, const char *node)
|
|
||||||
{
|
|
||||||
struct htab *htab_r = htab;
|
|
||||||
|
|
||||||
if (htab_r->first_free_entry >= htab_r->entry_tab_size)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct entry *ep;
|
|
||||||
unsigned modulus;
|
|
||||||
unsigned entry_tab_size;
|
|
||||||
|
|
||||||
/* Increase the number of hash entries, and re-hash the data.
|
|
||||||
The method of shrinking and increasing is made to compactify
|
|
||||||
the heap. If twice as much data would be allocated
|
|
||||||
straightforwardly, we would never re-use a byte of memory. */
|
|
||||||
|
|
||||||
/* Let htab shrink. Keep only the header, not the pointer vector. */
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xrealloc ((char *) htab_r, sizeof (struct htab));
|
|
||||||
|
|
||||||
modulus = 2 * htab_r->modulus;
|
|
||||||
entry_tab_size = 2 * htab_r->entry_tab_size;
|
|
||||||
|
|
||||||
/* Increase the number of possible entries. */
|
|
||||||
|
|
||||||
htab_r->entry_tab = (struct entry *)
|
|
||||||
xrealloc ((char *) htab_r->entry_tab,
|
|
||||||
sizeof (struct entry) * entry_tab_size);
|
|
||||||
|
|
||||||
/* Increase the size of htab again. */
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xrealloc ((char *) htab_r,
|
|
||||||
sizeof (struct htab) + sizeof (struct entry *) * modulus);
|
|
||||||
|
|
||||||
htab_r->modulus = modulus;
|
|
||||||
htab_r->entry_tab_size = entry_tab_size;
|
|
||||||
htab = htab_r;
|
|
||||||
|
|
||||||
i = htab_r->first_free_entry;
|
|
||||||
|
|
||||||
/* Make the increased hash table empty. The entries are still
|
|
||||||
available in htab->entry_tab. */
|
|
||||||
|
|
||||||
forget_all ();
|
|
||||||
|
|
||||||
/* Go through the entries and install them in the pointer vector
|
|
||||||
htab->hash. The items are actually inserted in htab->entry_tab at
|
|
||||||
the position where they already are. The htab->coll_link need
|
|
||||||
however be updated. Could be made a little more efficient. */
|
|
||||||
|
|
||||||
for (ep = htab_r->entry_tab; i > 0; i--)
|
|
||||||
{
|
|
||||||
hash_insert2 (htab_r, ep->ino, ep->dev, ep->node);
|
|
||||||
ep++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash_insert2 (htab_r, ino, dev, node);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/* For created inodes, a pointer in the search structure to this
|
|
||||||
character identifies the inode as being new. */
|
|
||||||
extern char new_file;
|
|
||||||
|
|
||||||
void hash_init PARAMS ((unsigned int modulus, unsigned int entry_tab_size));
|
|
||||||
void forget_all PARAMS ((void));
|
|
||||||
char *remember_copied PARAMS ((const char *node, ino_t ino, dev_t dev));
|
|
||||||
int remember_created PARAMS ((const char *path));
|
|
||||||
@@ -1,911 +0,0 @@
|
|||||||
/* cp.c -- file copying (main routines)
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2001 Free Software Foundation.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
Written by Torbjorn Granlund, David MacKenzie, and Jim Meyering. */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "argmatch.h"
|
|
||||||
#include "backupfile.h"
|
|
||||||
#include "copy.h"
|
|
||||||
#include "cp-hash.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "dirname.h"
|
|
||||||
#include "path-concat.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
#define ASSIGN_BASENAME_STRDUPA(Dest, File_name) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
char *tmp_abns_; \
|
|
||||||
ASSIGN_STRDUPA (tmp_abns_, (File_name)); \
|
|
||||||
strip_trailing_slashes (tmp_abns_); \
|
|
||||||
Dest = base_name (tmp_abns_); \
|
|
||||||
} \
|
|
||||||
while (0)
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "cp"
|
|
||||||
|
|
||||||
#define AUTHORS "Torbjorn Granlund, David MacKenzie, and Jim Meyering"
|
|
||||||
|
|
||||||
#ifndef _POSIX_VERSION
|
|
||||||
uid_t geteuid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Used by do_copy, make_path_private, and re_protect
|
|
||||||
to keep a list of leading directories whose protections
|
|
||||||
need to be fixed after copying. */
|
|
||||||
struct dir_attr
|
|
||||||
{
|
|
||||||
int is_new_dir;
|
|
||||||
int slash_offset;
|
|
||||||
struct dir_attr *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
|
|
||||||
SPARSE_OPTION,
|
|
||||||
STRIP_TRAILING_SLASHES_OPTION,
|
|
||||||
PARENTS_OPTION,
|
|
||||||
UNLINK_DEST_BEFORE_OPENING
|
|
||||||
};
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* Initial number of entries in each hash table entry's table of inodes. */
|
|
||||||
#define INITIAL_HASH_MODULE 100
|
|
||||||
|
|
||||||
/* Initial number of entries in the inode hash table. */
|
|
||||||
#define INITIAL_ENTRY_TAB_SIZE 70
|
|
||||||
|
|
||||||
/* The invocation name of this program. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
|
|
||||||
as its destination instead of the usual "e_dir/e_file." */
|
|
||||||
static int flag_path = 0;
|
|
||||||
|
|
||||||
/* Remove any trailing slashes from each SOURCE argument. */
|
|
||||||
static int remove_trailing_slashes;
|
|
||||||
|
|
||||||
static char const *const sparse_type_string[] =
|
|
||||||
{
|
|
||||||
"never", "auto", "always", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum Sparse_type const sparse_type[] =
|
|
||||||
{
|
|
||||||
SPARSE_NEVER, SPARSE_AUTO, SPARSE_ALWAYS
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The error code to return to the system. */
|
|
||||||
static int exit_status = 0;
|
|
||||||
|
|
||||||
static struct option const long_opts[] =
|
|
||||||
{
|
|
||||||
{"archive", no_argument, NULL, 'a'},
|
|
||||||
{"backup", optional_argument, NULL, 'b'},
|
|
||||||
{"dereference", no_argument, NULL, 'L'},
|
|
||||||
{"force", no_argument, NULL, 'f'},
|
|
||||||
{"sparse", required_argument, NULL, SPARSE_OPTION},
|
|
||||||
{"interactive", no_argument, NULL, 'i'},
|
|
||||||
{"link", no_argument, NULL, 'l'},
|
|
||||||
{"no-dereference", no_argument, NULL, 'd'},
|
|
||||||
{"one-file-system", no_argument, NULL, 'x'},
|
|
||||||
{"parents", no_argument, NULL, PARENTS_OPTION},
|
|
||||||
{"path", no_argument, NULL, PARENTS_OPTION}, /* Deprecated. */
|
|
||||||
{"preserve", no_argument, NULL, 'p'},
|
|
||||||
{"recursive", no_argument, NULL, 'R'},
|
|
||||||
{"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
|
|
||||||
{"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
|
|
||||||
{"suffix", required_argument, NULL, 'S'},
|
|
||||||
{"symbolic-link", no_argument, NULL, 's'},
|
|
||||||
{"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
|
|
||||||
{"update", no_argument, NULL, 'u'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... SOURCE DEST\n\
|
|
||||||
or: %s [OPTION]... SOURCE... DIRECTORY\n\
|
|
||||||
or: %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
|
|
||||||
\n\
|
|
||||||
-a, --archive same as -dpR\n\
|
|
||||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
|
||||||
-b like --backup but does not accept an argument\n\
|
|
||||||
-d, --no-dereference never follow symbolic links\n\
|
|
||||||
-f, --force if an existing destination file cannot be\n\
|
|
||||||
opened, remove it and try again\n\
|
|
||||||
-i, --interactive prompt before overwrite\n\
|
|
||||||
-H follow command-line symbolic links\n\
|
|
||||||
-l, --link link files instead of copying\n\
|
|
||||||
-L, --dereference always follow symbolic links\n\
|
|
||||||
-p, --preserve preserve file attributes if possible\n\
|
|
||||||
--parents append source path to DIRECTORY\n\
|
|
||||||
-P same as `--parents' for now; soon to change to\n\
|
|
||||||
`--no-dereference' to conform to POSIX\n\
|
|
||||||
-r copy recursively, non-directories as files\n\
|
|
||||||
WARNING: use -R instead when you might copy\n\
|
|
||||||
special files like FIFOs or /dev/zero\n\
|
|
||||||
--remove-destination remove each existing destination file before\n\
|
|
||||||
attempting to open it (contrast with --force)\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
--sparse=WHEN control creation of sparse files\n\
|
|
||||||
-R, --recursive copy directories recursively\n\
|
|
||||||
--strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
|
|
||||||
argument\n\
|
|
||||||
-s, --symbolic-link make symbolic links instead of copying\n\
|
|
||||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
|
||||||
--target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY\n\
|
|
||||||
-u, --update copy only when the SOURCE file is newer\n\
|
|
||||||
than the destination file or when the\n\
|
|
||||||
destination file is missing\n\
|
|
||||||
-v, --verbose explain what is being done\n\
|
|
||||||
-x, --one-file-system stay on this file system\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
By default, sparse SOURCE files are detected by a crude heuristic and the\n\
|
|
||||||
corresponding DEST file is made sparse as well. That is the behavior\n\
|
|
||||||
selected by --sparse=auto. Specify --sparse=always to create a sparse DEST\n\
|
|
||||||
file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\
|
|
||||||
Use --sparse=never to inhibit creation of sparse files.\n\
|
|
||||||
\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
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\
|
|
||||||
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\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
\n\
|
|
||||||
As a special case, cp makes a backup of SOURCE when the force and backup\n\
|
|
||||||
options are given and SOURCE and DEST are the same name for an existing,\n\
|
|
||||||
regular file.\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure that the parent directories of CONST_DST_PATH have the
|
|
||||||
correct protections, for the --parents option. This is done
|
|
||||||
after all copying has been completed, to allow permissions
|
|
||||||
that don't include user write/execute.
|
|
||||||
|
|
||||||
SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the
|
|
||||||
source directory name.
|
|
||||||
|
|
||||||
ATTR_LIST is a null-terminated linked list of structures that
|
|
||||||
indicates the end of the filename of each intermediate directory
|
|
||||||
in CONST_DST_PATH that may need to have its attributes changed.
|
|
||||||
The command `cp --parents --preserve a/b/c d/e_dir' changes the
|
|
||||||
attributes of the directories d/e_dir/a and d/e_dir/a/b to match
|
|
||||||
the corresponding source directories regardless of whether they
|
|
||||||
existed before the `cp' command was given.
|
|
||||||
|
|
||||||
Return 0 if the parent of CONST_DST_PATH and any intermediate
|
|
||||||
directories specified by ATTR_LIST have the proper permissions
|
|
||||||
when done, otherwise 1. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
re_protect (const char *const_dst_path, int src_offset,
|
|
||||||
struct dir_attr *attr_list, const struct cp_options *x)
|
|
||||||
{
|
|
||||||
struct dir_attr *p;
|
|
||||||
char *dst_path; /* A copy of CONST_DST_PATH we can change. */
|
|
||||||
char *src_path; /* The source name in `dst_path'. */
|
|
||||||
uid_t myeuid = geteuid ();
|
|
||||||
|
|
||||||
dst_path = (char *) alloca (strlen (const_dst_path) + 1);
|
|
||||||
strcpy (dst_path, const_dst_path);
|
|
||||||
src_path = dst_path + src_offset;
|
|
||||||
|
|
||||||
for (p = attr_list; p; p = p->next)
|
|
||||||
{
|
|
||||||
struct stat src_sb;
|
|
||||||
|
|
||||||
dst_path[p->slash_offset] = '\0';
|
|
||||||
|
|
||||||
if ((*(x->xstat)) (src_path, &src_sb))
|
|
||||||
{
|
|
||||||
error (0, errno, _("getting attributes of %s"),
|
|
||||||
quote (src_path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adjust the times (and if possible, ownership) for the copy.
|
|
||||||
chown turns off set[ug]id bits for non-root,
|
|
||||||
so do the chmod last. */
|
|
||||||
|
|
||||||
if (x->preserve_timestamps)
|
|
||||||
{
|
|
||||||
struct utimbuf utb;
|
|
||||||
|
|
||||||
/* There's currently no interface to set file timestamps with
|
|
||||||
better than 1-second resolution, so discard any fractional
|
|
||||||
part of the source timestamp. */
|
|
||||||
|
|
||||||
utb.actime = src_sb.st_atime;
|
|
||||||
utb.modtime = src_sb.st_mtime;
|
|
||||||
|
|
||||||
if (utime (dst_path, &utb))
|
|
||||||
{
|
|
||||||
error (0, errno, _("preserving times for %s"), quote (dst_path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->preserve_owner_and_group)
|
|
||||||
{
|
|
||||||
/* If non-root uses -p, it's ok if we can't preserve ownership.
|
|
||||||
But root probably wants to know, e.g. if NFS disallows it,
|
|
||||||
or if the target system doesn't support file ownership. */
|
|
||||||
if (chown (dst_path, src_sb.st_uid, src_sb.st_gid)
|
|
||||||
&& ((errno != EPERM && errno != EINVAL) || myeuid == 0))
|
|
||||||
{
|
|
||||||
error (0, errno, _("preserving ownership for %s"),
|
|
||||||
quote (dst_path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->preserve_chmod_bits || p->is_new_dir)
|
|
||||||
{
|
|
||||||
if (chmod (dst_path, src_sb.st_mode & x->umask_kill))
|
|
||||||
{
|
|
||||||
error (0, errno, _("preserving permissions for %s"),
|
|
||||||
quote (dst_path));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dst_path[p->slash_offset] = '/';
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure that the parent directory of CONST_DIRPATH exists, for
|
|
||||||
the --parents option.
|
|
||||||
|
|
||||||
SRC_OFFSET is the index in CONST_DIRPATH (which is a destination
|
|
||||||
path) of the beginning of the source directory name.
|
|
||||||
Create any leading directories that don't already exist,
|
|
||||||
giving them permissions MODE.
|
|
||||||
If VERBOSE_FMT_STRING is nonzero, use it as a printf format
|
|
||||||
string for printing a message after successfully making a directory.
|
|
||||||
The format should take two string arguments: the names of the
|
|
||||||
source and destination directories.
|
|
||||||
Creates a linked list of attributes of intermediate directories,
|
|
||||||
*ATTR_LIST, for re_protect to use after calling copy.
|
|
||||||
Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH.
|
|
||||||
|
|
||||||
Return 0 if parent of CONST_DIRPATH exists as a directory with the proper
|
|
||||||
permissions when done, otherwise 1. */
|
|
||||||
|
|
||||||
/* FIXME: find a way to synch this function with the one in lib/makepath.c. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
make_path_private (const char *const_dirpath, int src_offset, int mode,
|
|
||||||
const char *verbose_fmt_string, struct dir_attr **attr_list,
|
|
||||||
int *new_dst, int (*xstat)())
|
|
||||||
{
|
|
||||||
struct stat stats;
|
|
||||||
char *dirpath; /* A copy of CONST_DIRPATH we can change. */
|
|
||||||
char *src; /* Source name in `dirpath'. */
|
|
||||||
char *tmp_dst_dirname; /* Leading path of `dirpath', malloc. */
|
|
||||||
char *dst_dirname; /* Leading path of `dirpath', alloca. */
|
|
||||||
|
|
||||||
dirpath = (char *) alloca (strlen (const_dirpath) + 1);
|
|
||||||
strcpy (dirpath, const_dirpath);
|
|
||||||
|
|
||||||
src = dirpath + src_offset;
|
|
||||||
|
|
||||||
tmp_dst_dirname = dir_name (dirpath);
|
|
||||||
dst_dirname = (char *) alloca (strlen (tmp_dst_dirname) + 1);
|
|
||||||
strcpy (dst_dirname, tmp_dst_dirname);
|
|
||||||
free (tmp_dst_dirname);
|
|
||||||
|
|
||||||
*attr_list = NULL;
|
|
||||||
|
|
||||||
if ((*xstat) (dst_dirname, &stats))
|
|
||||||
{
|
|
||||||
/* Parent of CONST_DIRNAME does not exist.
|
|
||||||
Make all missing intermediate directories. */
|
|
||||||
char *slash;
|
|
||||||
|
|
||||||
slash = src;
|
|
||||||
while (*slash == '/')
|
|
||||||
slash++;
|
|
||||||
while ((slash = strchr (slash, '/')))
|
|
||||||
{
|
|
||||||
/* Add this directory to the list of directories whose modes need
|
|
||||||
fixing later. */
|
|
||||||
struct dir_attr *new =
|
|
||||||
(struct dir_attr *) xmalloc (sizeof (struct dir_attr));
|
|
||||||
new->slash_offset = slash - dirpath;
|
|
||||||
new->next = *attr_list;
|
|
||||||
*attr_list = new;
|
|
||||||
|
|
||||||
*slash = '\0';
|
|
||||||
if ((*xstat) (dirpath, &stats))
|
|
||||||
{
|
|
||||||
/* This element of the path does not exist. We must set
|
|
||||||
*new_dst and new->is_new_dir inside this loop because,
|
|
||||||
for example, in the command `cp --parents ../a/../b/c e_dir',
|
|
||||||
make_path_private creates only e_dir/../a if ./b already
|
|
||||||
exists. */
|
|
||||||
*new_dst = 1;
|
|
||||||
new->is_new_dir = 1;
|
|
||||||
if (mkdir (dirpath, mode))
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot make directory %s"),
|
|
||||||
quote (dirpath));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (verbose_fmt_string != NULL)
|
|
||||||
printf (verbose_fmt_string, src, dirpath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!S_ISDIR (stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s exists but is not a directory"),
|
|
||||||
quote (dirpath));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new->is_new_dir = 0;
|
|
||||||
*new_dst = 0;
|
|
||||||
}
|
|
||||||
*slash++ = '/';
|
|
||||||
|
|
||||||
/* Avoid unnecessary calls to `stat' when given
|
|
||||||
pathnames containing multiple adjacent slashes. */
|
|
||||||
while (*slash == '/')
|
|
||||||
slash++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We get here if the parent of `dirpath' already exists. */
|
|
||||||
|
|
||||||
else if (!S_ISDIR (stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s exists but is not a directory"), quote (dst_dirname));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*new_dst = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the arguments, and copy each by calling copy.
|
|
||||||
Return 0 if successful, 1 if any errors occur. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
do_copy (int n_files, char **file, const char *target_directory,
|
|
||||||
const struct cp_options *x)
|
|
||||||
{
|
|
||||||
const char *dest;
|
|
||||||
struct stat sb;
|
|
||||||
int new_dst = 0;
|
|
||||||
int ret = 0;
|
|
||||||
int dest_is_dir = 0;
|
|
||||||
|
|
||||||
if (n_files <= 0)
|
|
||||||
{
|
|
||||||
error (0, 0, _("missing file arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
if (n_files == 1 && !target_directory)
|
|
||||||
{
|
|
||||||
error (0, 0, _("missing destination file"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_directory)
|
|
||||||
dest = target_directory;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dest = file[n_files - 1];
|
|
||||||
--n_files;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lstat (dest, &sb))
|
|
||||||
{
|
|
||||||
if (errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("accessing %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_dst = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct stat sbx;
|
|
||||||
|
|
||||||
/* If `dest' is not a symlink to a nonexistent file, use
|
|
||||||
the results of stat instead of lstat, so we can copy files
|
|
||||||
into symlinks to directories. */
|
|
||||||
if (stat (dest, &sbx) == 0)
|
|
||||||
sb = sbx;
|
|
||||||
|
|
||||||
dest_is_dir = S_ISDIR (sb.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dest_is_dir)
|
|
||||||
{
|
|
||||||
if (target_directory)
|
|
||||||
{
|
|
||||||
error (0, 0, _("specified target, %s is not a directory"),
|
|
||||||
quote (dest));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_files > 1)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("copying multiple files, but last argument %s is not a directory"),
|
|
||||||
quote (dest));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dest_is_dir)
|
|
||||||
{
|
|
||||||
/* cp file1...filen edir
|
|
||||||
Copy the files `file1' through `filen'
|
|
||||||
to the existing directory `edir'. */
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < n_files; i++)
|
|
||||||
{
|
|
||||||
char *dst_path;
|
|
||||||
int parent_exists = 1; /* True if dir_name (dst_path) exists. */
|
|
||||||
struct dir_attr *attr_list;
|
|
||||||
char *arg_in_concat = NULL;
|
|
||||||
char *arg = file[i];
|
|
||||||
|
|
||||||
/* Trailing slashes are meaningful (i.e., maybe worth preserving)
|
|
||||||
only in the source file names. */
|
|
||||||
if (remove_trailing_slashes)
|
|
||||||
strip_trailing_slashes (arg);
|
|
||||||
|
|
||||||
if (flag_path)
|
|
||||||
{
|
|
||||||
char *arg_no_trailing_slash;
|
|
||||||
|
|
||||||
/* Use `arg' without trailing slashes in constructing destination
|
|
||||||
file names. Otherwise, we can end up trying to create a
|
|
||||||
directory via `mkdir ("dst/foo/"...', which is not portable.
|
|
||||||
It fails, due to the trailing slash, on at least
|
|
||||||
NetBSD 1.[34] systems. */
|
|
||||||
ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
|
|
||||||
strip_trailing_slashes (arg_no_trailing_slash);
|
|
||||||
|
|
||||||
/* Append all of `arg' (minus any trailing slash) to `dest'. */
|
|
||||||
dst_path = path_concat (dest, arg_no_trailing_slash,
|
|
||||||
&arg_in_concat);
|
|
||||||
if (dst_path == NULL)
|
|
||||||
xalloc_die ();
|
|
||||||
|
|
||||||
/* For --parents, we have to make sure that the directory
|
|
||||||
dir_name (dst_path) exists. We may have to create a few
|
|
||||||
leading directories. */
|
|
||||||
parent_exists = !make_path_private (dst_path,
|
|
||||||
arg_in_concat - dst_path,
|
|
||||||
S_IRWXU,
|
|
||||||
(x->verbose
|
|
||||||
? "%s -> %s\n" : NULL),
|
|
||||||
&attr_list, &new_dst,
|
|
||||||
x->xstat);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *arg_base;
|
|
||||||
/* Append the last component of `arg' to `dest'. */
|
|
||||||
|
|
||||||
ASSIGN_BASENAME_STRDUPA (arg_base, arg);
|
|
||||||
/* For `cp -R source/.. dest', don't copy into `dest/..'. */
|
|
||||||
dst_path = (STREQ (arg_base, "..")
|
|
||||||
? xstrdup (dest)
|
|
||||||
: path_concat (dest, arg_base, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parent_exists)
|
|
||||||
{
|
|
||||||
/* make_path_private failed, so don't even attempt the copy. */
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int copy_into_self;
|
|
||||||
ret |= copy (arg, dst_path, new_dst, x, ©_into_self, NULL);
|
|
||||||
forget_all ();
|
|
||||||
|
|
||||||
if (flag_path)
|
|
||||||
{
|
|
||||||
ret |= re_protect (dst_path, arg_in_concat - dst_path,
|
|
||||||
attr_list, x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free (dst_path);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else /* if (n_files == 1) */
|
|
||||||
{
|
|
||||||
char *new_dest;
|
|
||||||
char *source;
|
|
||||||
int unused;
|
|
||||||
struct stat source_stats;
|
|
||||||
|
|
||||||
if (flag_path)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("when preserving paths, the destination must be a directory"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
source = file[0];
|
|
||||||
|
|
||||||
/* When the force and backup options have been specified and
|
|
||||||
the source and destination are the same name for an existing
|
|
||||||
regular file, convert the user's command, e.g.,
|
|
||||||
`cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
|
|
||||||
where SUFFIX is determined by any version control options used. */
|
|
||||||
|
|
||||||
if (x->unlink_dest_after_failed_open
|
|
||||||
&& x->backup_type != none
|
|
||||||
&& STREQ (source, dest)
|
|
||||||
&& !new_dst && S_ISREG (sb.st_mode))
|
|
||||||
{
|
|
||||||
static struct cp_options x_tmp;
|
|
||||||
|
|
||||||
new_dest = find_backup_file_name (dest, x->backup_type);
|
|
||||||
/* Set x->backup_type to `none' so that the normal backup
|
|
||||||
mechanism is not used when performing the actual copy.
|
|
||||||
backup_type must be set to `none' only *after* the above
|
|
||||||
call to find_backup_file_name -- that function uses
|
|
||||||
backup_type to determine the suffix it applies. */
|
|
||||||
x_tmp = *x;
|
|
||||||
x_tmp.backup_type = none;
|
|
||||||
x = &x_tmp;
|
|
||||||
|
|
||||||
if (new_dest == NULL)
|
|
||||||
xalloc_die ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When the destination is specified with a trailing slash and the
|
|
||||||
source exists but is not a directory, convert the user's command
|
|
||||||
`cp source dest/' to `cp source dest/basename(source)'. Doing
|
|
||||||
this ensures that the command `cp non-directory file/' will now
|
|
||||||
fail rather than performing the copy. COPY diagnoses the case of
|
|
||||||
`cp directory non-directory'. */
|
|
||||||
|
|
||||||
else if (dest[strlen (dest) - 1] == '/'
|
|
||||||
&& lstat (source, &source_stats) == 0
|
|
||||||
&& !S_ISDIR (source_stats.st_mode))
|
|
||||||
{
|
|
||||||
char *source_base;
|
|
||||||
|
|
||||||
ASSIGN_BASENAME_STRDUPA (source_base, source);
|
|
||||||
new_dest = (char *) alloca (strlen (dest)
|
|
||||||
+ strlen (source_base) + 1);
|
|
||||||
stpcpy (stpcpy (new_dest, dest), source_base);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_dest = (char *) dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy (source, new_dest, new_dst, x, &unused, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unreachable */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
cp_option_init (struct cp_options *x)
|
|
||||||
{
|
|
||||||
x->copy_as_regular = 1;
|
|
||||||
x->dereference = DEREF_UNDEFINED;
|
|
||||||
x->unlink_dest_before_opening = 0;
|
|
||||||
x->unlink_dest_after_failed_open = 0;
|
|
||||||
x->failed_unlink_is_fatal = 1;
|
|
||||||
x->hard_link = 0;
|
|
||||||
x->interactive = 0;
|
|
||||||
x->myeuid = geteuid ();
|
|
||||||
x->move_mode = 0;
|
|
||||||
x->one_file_system = 0;
|
|
||||||
|
|
||||||
x->preserve_owner_and_group = 0;
|
|
||||||
x->preserve_chmod_bits = 0;
|
|
||||||
x->preserve_timestamps = 0;
|
|
||||||
|
|
||||||
x->require_preserve = 0;
|
|
||||||
x->recursive = 0;
|
|
||||||
x->sparse_mode = SPARSE_AUTO;
|
|
||||||
x->symbolic_link = 0;
|
|
||||||
x->set_mode = 0;
|
|
||||||
x->mode = 0;
|
|
||||||
|
|
||||||
/* Find out the current file creation mask, to knock the right bits
|
|
||||||
when using chmod. The creation mask is set to be liberal, so
|
|
||||||
that created directories can be written, even if it would not
|
|
||||||
have been allowed with the mask this process was started with. */
|
|
||||||
x->umask_kill = ~ umask (0);
|
|
||||||
|
|
||||||
x->update = 0;
|
|
||||||
x->verbose = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
int make_backups = 0;
|
|
||||||
char *backup_suffix_string;
|
|
||||||
char *version_control_string = NULL;
|
|
||||||
struct cp_options x;
|
|
||||||
char *target_directory = NULL;
|
|
||||||
int used_P_option = 0;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
cp_option_init (&x);
|
|
||||||
|
|
||||||
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
|
|
||||||
we'll actually use backup_suffix_string. */
|
|
||||||
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:", long_opts, NULL))
|
|
||||||
!= -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SPARSE_OPTION:
|
|
||||||
x.sparse_mode = XARGMATCH ("--sparse", optarg,
|
|
||||||
sparse_type_string, sparse_type);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'a': /* Like -dpR. */
|
|
||||||
x.dereference = DEREF_NEVER;
|
|
||||||
x.preserve_owner_and_group = 1;
|
|
||||||
x.preserve_chmod_bits = 1;
|
|
||||||
x.preserve_timestamps = 1;
|
|
||||||
x.require_preserve = 1;
|
|
||||||
x.recursive = 1;
|
|
||||||
x.copy_as_regular = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'V': /* FIXME: this is deprecated. Remove it in 2001. */
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: --version-control (-V) is obsolete; support for\
|
|
||||||
it\nwill be removed in some future release. Use --backup=%s instead."
|
|
||||||
), optarg);
|
|
||||||
/* Fall through. */
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
make_backups = 1;
|
|
||||||
if (optarg)
|
|
||||||
version_control_string = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
|
||||||
x.dereference = DEREF_NEVER;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
x.unlink_dest_after_failed_open = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'H':
|
|
||||||
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'i':
|
|
||||||
x.interactive = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
x.hard_link = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
x.dereference = DEREF_ALWAYS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
x.preserve_owner_and_group = 1;
|
|
||||||
x.preserve_chmod_bits = 1;
|
|
||||||
x.preserve_timestamps = 1;
|
|
||||||
x.require_preserve = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'P':
|
|
||||||
used_P_option = 1;
|
|
||||||
/* fall through */
|
|
||||||
case PARENTS_OPTION:
|
|
||||||
flag_path = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r':
|
|
||||||
x.recursive = 1;
|
|
||||||
x.copy_as_regular = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R':
|
|
||||||
x.recursive = 1;
|
|
||||||
x.copy_as_regular = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UNLINK_DEST_BEFORE_OPENING:
|
|
||||||
x.unlink_dest_before_opening = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STRIP_TRAILING_SLASHES_OPTION:
|
|
||||||
remove_trailing_slashes = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
x.symbolic_link = 1;
|
|
||||||
#else
|
|
||||||
error (1, 0, _("symbolic links are not supported on this system"));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TARGET_DIRECTORY_OPTION:
|
|
||||||
target_directory = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'u':
|
|
||||||
x.update = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'v':
|
|
||||||
x.verbose = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
x.one_file_system = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'S':
|
|
||||||
make_backups = 1;
|
|
||||||
backup_suffix_string = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x.hard_link && x.symbolic_link)
|
|
||||||
{
|
|
||||||
error (0, 0, _("cannot make both hard and symbolic links"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (used_P_option)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("\
|
|
||||||
Warning: the meaning of `-P' will change in the future to conform to POSIX.\n\
|
|
||||||
Use `--parents' for the old meaning, and `--no-dereference' for the new one."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backup_suffix_string)
|
|
||||||
simple_backup_suffix = xstrdup (backup_suffix_string);
|
|
||||||
|
|
||||||
x.backup_type = (make_backups
|
|
||||||
? xget_version (_("backup type"),
|
|
||||||
version_control_string)
|
|
||||||
: none);
|
|
||||||
|
|
||||||
if (x.preserve_chmod_bits == 1)
|
|
||||||
x.umask_kill = ~ (mode_t) 0;
|
|
||||||
|
|
||||||
if (x.dereference == DEREF_UNDEFINED)
|
|
||||||
{
|
|
||||||
if (x.recursive)
|
|
||||||
/* This is compatible with FreeBSD. */
|
|
||||||
x.dereference = DEREF_NEVER;
|
|
||||||
else
|
|
||||||
x.dereference = DEREF_ALWAYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The key difference between -d (--no-dereference) and not is the version
|
|
||||||
of `stat' to call. */
|
|
||||||
|
|
||||||
if (x.dereference == DEREF_NEVER)
|
|
||||||
x.xstat = lstat;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* For DEREF_COMMAND_LINE_ARGUMENTS, x.xstat must be stat for
|
|
||||||
each command line argument, but must later be `lstat' for
|
|
||||||
any symlinks that are found via recursive traversal. */
|
|
||||||
x.xstat = stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If --force (-f) was specified and we're in link-creation mode,
|
|
||||||
first remove any existing destination file. */
|
|
||||||
if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link))
|
|
||||||
x.unlink_dest_before_opening = 1;
|
|
||||||
|
|
||||||
/* Allocate space for remembering copied and created files. */
|
|
||||||
|
|
||||||
hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
|
|
||||||
|
|
||||||
exit_status |= do_copy (argc - optind, argv + optind, target_directory, &x);
|
|
||||||
|
|
||||||
exit (exit_status);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,952 +0,0 @@
|
|||||||
/* df - summarize free disk space
|
|
||||||
Copyright (C) 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by David MacKenzie <[email protected]>.
|
|
||||||
--human-readable and --megabyte options added by [email protected].
|
|
||||||
--si and large file support added by [email protected]. */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#if HAVE_INTTYPES_H
|
|
||||||
# include <inttypes.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "dirname.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "fsusage.h"
|
|
||||||
#include "human.h"
|
|
||||||
#include "mountlist.h"
|
|
||||||
#include "path-concat.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "save-cwd.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "df"
|
|
||||||
|
|
||||||
#define AUTHORS \
|
|
||||||
"Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
char *xgetcwd ();
|
|
||||||
|
|
||||||
/* Name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, show inode information. */
|
|
||||||
static int inode_format;
|
|
||||||
|
|
||||||
/* If nonzero, show even filesystems with zero size or
|
|
||||||
uninteresting types. */
|
|
||||||
static int show_all_fs;
|
|
||||||
|
|
||||||
/* If nonzero, show only local filesystems. */
|
|
||||||
static int show_local_fs;
|
|
||||||
|
|
||||||
/* If nonzero, output data for each filesystem corresponding to a
|
|
||||||
command line argument -- even if it's a dummy (automounter) entry. */
|
|
||||||
static int show_listed_fs;
|
|
||||||
|
|
||||||
/* If positive, the units to use when printing sizes;
|
|
||||||
if negative, the human-readable base. */
|
|
||||||
static int output_block_size;
|
|
||||||
|
|
||||||
/* If nonzero, use the POSIX output format. */
|
|
||||||
static int posix_format;
|
|
||||||
|
|
||||||
/* If nonzero, invoke the `sync' system call before getting any usage data.
|
|
||||||
Using this option can make df very slow, especially with many or very
|
|
||||||
busy disks. Note that this may make a difference on some systems --
|
|
||||||
SunOs4.1.3, for one. It is *not* necessary on Linux. */
|
|
||||||
static int require_sync = 0;
|
|
||||||
|
|
||||||
/* Nonzero if errors have occurred. */
|
|
||||||
static int exit_status;
|
|
||||||
|
|
||||||
/* A filesystem type to display. */
|
|
||||||
|
|
||||||
struct fs_type_list
|
|
||||||
{
|
|
||||||
char *fs_name;
|
|
||||||
struct fs_type_list *fs_next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Linked list of filesystem types to display.
|
|
||||||
If `fs_select_list' is NULL, list all types.
|
|
||||||
This table is generated dynamically from command-line options,
|
|
||||||
rather than hardcoding into the program what it thinks are the
|
|
||||||
valid filesystem types; let the user specify any filesystem type
|
|
||||||
they want to, and if there are any filesystems of that type, they
|
|
||||||
will be shown.
|
|
||||||
|
|
||||||
Some filesystem types:
|
|
||||||
4.2 4.3 ufs nfs swap ignore io vm efs dbg */
|
|
||||||
|
|
||||||
static struct fs_type_list *fs_select_list;
|
|
||||||
|
|
||||||
/* Linked list of filesystem types to omit.
|
|
||||||
If the list is empty, don't exclude any types. */
|
|
||||||
|
|
||||||
static struct fs_type_list *fs_exclude_list;
|
|
||||||
|
|
||||||
/* Linked list of mounted filesystems. */
|
|
||||||
static struct mount_entry *mount_list;
|
|
||||||
|
|
||||||
/* If nonzero, print filesystem type as well. */
|
|
||||||
static int print_type;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
SYNC_OPTION = CHAR_MAX + 1,
|
|
||||||
NO_SYNC_OPTION,
|
|
||||||
BLOCK_SIZE_OPTION
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"all", no_argument, NULL, 'a'},
|
|
||||||
{"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
|
|
||||||
{"inodes", no_argument, NULL, 'i'},
|
|
||||||
{"human-readable", no_argument, NULL, 'h'},
|
|
||||||
{"si", no_argument, NULL, 'H'},
|
|
||||||
{"kilobytes", no_argument, NULL, 'k'},
|
|
||||||
{"local", no_argument, NULL, 'l'},
|
|
||||||
{"megabytes", no_argument, NULL, 'm'},
|
|
||||||
{"portability", no_argument, NULL, 'P'},
|
|
||||||
{"print-type", no_argument, NULL, 'T'},
|
|
||||||
{"sync", no_argument, NULL, SYNC_OPTION},
|
|
||||||
{"no-sync", no_argument, NULL, NO_SYNC_OPTION},
|
|
||||||
{"type", required_argument, NULL, 't'},
|
|
||||||
{"exclude-type", required_argument, NULL, 'x'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_header (void)
|
|
||||||
{
|
|
||||||
printf (_("Filesystem "));
|
|
||||||
|
|
||||||
if (print_type)
|
|
||||||
printf (_(" Type"));
|
|
||||||
else
|
|
||||||
printf (" ");
|
|
||||||
|
|
||||||
if (inode_format)
|
|
||||||
printf (_(" Inodes IUsed IFree IUse%%"));
|
|
||||||
else if (output_block_size < 0)
|
|
||||||
printf (_(" Size Used Avail Use%%"));
|
|
||||||
else if (posix_format)
|
|
||||||
printf (_(" %4d-blocks Used Available Capacity"), output_block_size);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char buf[LONGEST_HUMAN_READABLE + 1];
|
|
||||||
char *p = human_readable (output_block_size, buf, 1, -1024);
|
|
||||||
|
|
||||||
/* Replace e.g. "1.0k" by "1k". */
|
|
||||||
size_t plen = strlen (p);
|
|
||||||
if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
|
|
||||||
strcpy (p + plen - 3, p + plen - 1);
|
|
||||||
|
|
||||||
printf (_(" %4s-blocks Used Available Use%%"), p);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf (_(" Mounted on\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If FSTYPE is a type of filesystem that should be listed,
|
|
||||||
return nonzero, else zero. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
selected_fstype (const char *fstype)
|
|
||||||
{
|
|
||||||
const struct fs_type_list *fsp;
|
|
||||||
|
|
||||||
if (fs_select_list == NULL || fstype == NULL)
|
|
||||||
return 1;
|
|
||||||
for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
|
|
||||||
if (STREQ (fstype, fsp->fs_name))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If FSTYPE is a type of filesystem that should be omitted,
|
|
||||||
return nonzero, else zero. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
excluded_fstype (const char *fstype)
|
|
||||||
{
|
|
||||||
const struct fs_type_list *fsp;
|
|
||||||
|
|
||||||
if (fs_exclude_list == NULL || fstype == NULL)
|
|
||||||
return 0;
|
|
||||||
for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
|
|
||||||
if (STREQ (fstype, fsp->fs_name))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Like human_readable_inexact, except return "-" if the argument is -1,
|
|
||||||
and if NEGATIVE is 1 then N represents a negative number, expressed
|
|
||||||
in two's complement. */
|
|
||||||
static char const *
|
|
||||||
df_readable (int negative, uintmax_t n, char *buf,
|
|
||||||
int from_block_size, int t_output_block_size,
|
|
||||||
enum human_inexact_style s)
|
|
||||||
{
|
|
||||||
if (n == -1)
|
|
||||||
return "-";
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *p = human_readable_inexact (negative ? - n : n,
|
|
||||||
buf + negative, from_block_size,
|
|
||||||
t_output_block_size,
|
|
||||||
negative ? - s : s);
|
|
||||||
if (negative)
|
|
||||||
*--p = '-';
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Display a space listing for the disk device with absolute path DISK.
|
|
||||||
If MOUNT_POINT is non-NULL, it is the path of the root of the
|
|
||||||
filesystem on DISK.
|
|
||||||
If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
|
|
||||||
If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
|
|
||||||
not be able to produce statistics in this case.
|
|
||||||
ME_DUMMY and ME_REMOTE are the mount entry flags. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
show_dev (const char *disk, const char *mount_point, const char *fstype,
|
|
||||||
int me_dummy, int me_remote)
|
|
||||||
{
|
|
||||||
struct fs_usage fsu;
|
|
||||||
const char *stat_file;
|
|
||||||
char buf[3][LONGEST_HUMAN_READABLE + 2];
|
|
||||||
int width;
|
|
||||||
int use_width;
|
|
||||||
int input_units;
|
|
||||||
int output_units;
|
|
||||||
uintmax_t total;
|
|
||||||
uintmax_t available;
|
|
||||||
int negate_available;
|
|
||||||
uintmax_t available_to_root;
|
|
||||||
uintmax_t used;
|
|
||||||
int negate_used;
|
|
||||||
double pct = -1;
|
|
||||||
|
|
||||||
if (me_remote && show_local_fs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (me_dummy && show_all_fs == 0 && !show_listed_fs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!selected_fstype (fstype) || excluded_fstype (fstype))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
|
|
||||||
program reports on the filesystem that the special file is on.
|
|
||||||
It would be better to report on the unmounted filesystem,
|
|
||||||
but statfs doesn't do that on most systems. */
|
|
||||||
stat_file = mount_point ? mount_point : disk;
|
|
||||||
|
|
||||||
if (get_fs_usage (stat_file, disk, &fsu))
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (stat_file));
|
|
||||||
exit_status = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (! disk)
|
|
||||||
disk = "-"; /* unknown */
|
|
||||||
if (! fstype)
|
|
||||||
fstype = "-"; /* unknown */
|
|
||||||
|
|
||||||
/* df.c reserved 5 positions for fstype,
|
|
||||||
but that does not suffice for type iso9660 */
|
|
||||||
if (print_type)
|
|
||||||
{
|
|
||||||
int disk_name_len = (int) strlen (disk);
|
|
||||||
int fstype_len = (int) strlen (fstype);
|
|
||||||
if (disk_name_len + fstype_len + 2 < 20)
|
|
||||||
printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
|
|
||||||
else if (!posix_format)
|
|
||||||
printf ("%s\n%18s ", disk, fstype);
|
|
||||||
else
|
|
||||||
printf ("%s %s", disk, fstype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((int) strlen (disk) > 20 && !posix_format)
|
|
||||||
printf ("%s\n%20s", disk, "");
|
|
||||||
else
|
|
||||||
printf ("%-20s", disk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inode_format)
|
|
||||||
{
|
|
||||||
width = 7;
|
|
||||||
use_width = 5;
|
|
||||||
input_units = 1;
|
|
||||||
output_units = output_block_size < 0 ? output_block_size : 1;
|
|
||||||
total = fsu.fsu_files;
|
|
||||||
available = fsu.fsu_ffree;
|
|
||||||
negate_available = 0;
|
|
||||||
available_to_root = available;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
width = output_block_size < 0 ? 5 : 9;
|
|
||||||
use_width = posix_format ? 8 : 4;
|
|
||||||
input_units = fsu.fsu_blocksize;
|
|
||||||
output_units = output_block_size;
|
|
||||||
total = fsu.fsu_blocks;
|
|
||||||
available = fsu.fsu_bavail;
|
|
||||||
negate_available = fsu.fsu_bavail_top_bit_set;
|
|
||||||
available_to_root = fsu.fsu_bfree;
|
|
||||||
}
|
|
||||||
|
|
||||||
used = -1;
|
|
||||||
negate_used = 0;
|
|
||||||
if (total != -1 && available_to_root != -1)
|
|
||||||
{
|
|
||||||
used = total - available_to_root;
|
|
||||||
if (total < available_to_root)
|
|
||||||
{
|
|
||||||
negate_used = 1;
|
|
||||||
used = - used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf (" %*s %*s %*s ",
|
|
||||||
width, df_readable (0, total,
|
|
||||||
buf[0], input_units, output_units,
|
|
||||||
(posix_format
|
|
||||||
? human_ceiling
|
|
||||||
: human_round_to_even)),
|
|
||||||
width, df_readable (negate_used, used,
|
|
||||||
buf[1], input_units, output_units,
|
|
||||||
human_ceiling),
|
|
||||||
width, df_readable (negate_available, available,
|
|
||||||
buf[2], input_units, output_units,
|
|
||||||
posix_format ? human_ceiling : human_floor));
|
|
||||||
|
|
||||||
if (used == -1 || available == -1)
|
|
||||||
;
|
|
||||||
else if (!negate_used
|
|
||||||
&& used <= TYPE_MAXIMUM (uintmax_t) / 100
|
|
||||||
&& used + available != 0
|
|
||||||
&& (used + available < used) == negate_available)
|
|
||||||
{
|
|
||||||
uintmax_t u100 = used * 100;
|
|
||||||
uintmax_t nonroot_total = used + available;
|
|
||||||
pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* The calculation cannot be done easily with integer
|
|
||||||
arithmetic. Fall back on floating point. This can suffer
|
|
||||||
from minor rounding errors, but doing it exactly requires
|
|
||||||
multiple precision arithmetic, and it's not worth the
|
|
||||||
aggravation. */
|
|
||||||
double u = negate_used ? - (double) - used : used;
|
|
||||||
double a = negate_available ? - (double) - available : available;
|
|
||||||
double nonroot_total = u + a;
|
|
||||||
if (nonroot_total)
|
|
||||||
{
|
|
||||||
double ipct;
|
|
||||||
pct = u * 100 / nonroot_total;
|
|
||||||
ipct = (long) pct;
|
|
||||||
|
|
||||||
/* Like `pct = ceil (dpct);', but avoid ceil so that
|
|
||||||
the math library needn't be linked. */
|
|
||||||
if (ipct - 1 < pct && pct <= ipct + 1)
|
|
||||||
pct = ipct + (ipct < pct);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 <= pct)
|
|
||||||
printf ("%*.0f%%", use_width - 1, pct);
|
|
||||||
else
|
|
||||||
printf ("%*s", use_width, "- ");
|
|
||||||
|
|
||||||
if (mount_point)
|
|
||||||
{
|
|
||||||
#ifdef HIDE_AUTOMOUNT_PREFIX
|
|
||||||
/* Don't print the first directory name in MOUNT_POINT if it's an
|
|
||||||
artifact of an automounter. This is a bit too aggressive to be
|
|
||||||
the default. */
|
|
||||||
if (strncmp ("/auto/", mount_point, 6) == 0)
|
|
||||||
mount_point += 5;
|
|
||||||
else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
|
|
||||||
mount_point += 8;
|
|
||||||
#endif
|
|
||||||
printf (" %s", mount_point);
|
|
||||||
}
|
|
||||||
putchar ('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Identify the directory, if any, that device
|
|
||||||
DISK is mounted on, and show its disk usage. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
show_disk (const char *disk)
|
|
||||||
{
|
|
||||||
struct mount_entry *me;
|
|
||||||
|
|
||||||
for (me = mount_list; me; me = me->me_next)
|
|
||||||
if (STREQ (disk, me->me_devname))
|
|
||||||
{
|
|
||||||
show_dev (me->me_devname, me->me_mountdir, me->me_type,
|
|
||||||
me->me_dummy, me->me_remote);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* No filesystem is mounted on DISK. */
|
|
||||||
show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the root mountpoint of the filesystem on which FILE exists, in
|
|
||||||
malloced storage. FILE_STAT should be the result of stating FILE. */
|
|
||||||
static char *
|
|
||||||
find_mount_point (const char *file, const struct stat *file_stat)
|
|
||||||
{
|
|
||||||
struct saved_cwd cwd;
|
|
||||||
struct stat last_stat;
|
|
||||||
char *mp = 0; /* The malloced mount point path. */
|
|
||||||
|
|
||||||
if (save_cwd (&cwd))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (S_ISDIR (file_stat->st_mode))
|
|
||||||
/* FILE is a directory, so just chdir there directly. */
|
|
||||||
{
|
|
||||||
last_stat = *file_stat;
|
|
||||||
if (chdir (file) < 0)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
/* FILE is some other kind of file, we need to use its directory. */
|
|
||||||
{
|
|
||||||
int rv;
|
|
||||||
char *tmp = xstrdup (file);
|
|
||||||
char *dir;
|
|
||||||
|
|
||||||
strip_trailing_slashes (tmp);
|
|
||||||
dir = dir_name (tmp);
|
|
||||||
free (tmp);
|
|
||||||
rv = chdir (dir);
|
|
||||||
free (dir);
|
|
||||||
|
|
||||||
if (rv < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (stat (".", &last_stat) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now walk up FILE's parents until we find another filesystem or /,
|
|
||||||
chdiring as we go. LAST_STAT holds stat information for the last place
|
|
||||||
we visited. */
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
if (stat ("..", &st) < 0)
|
|
||||||
goto done;
|
|
||||||
if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
|
|
||||||
/* cwd is the mount point. */
|
|
||||||
break;
|
|
||||||
if (chdir ("..") < 0)
|
|
||||||
goto done;
|
|
||||||
last_stat = st;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally reached a mount point, see what it's called. */
|
|
||||||
mp = xgetcwd ();
|
|
||||||
|
|
||||||
done:
|
|
||||||
/* Restore the original cwd. */
|
|
||||||
{
|
|
||||||
int save_errno = errno;
|
|
||||||
if (restore_cwd (&cwd, 0, mp))
|
|
||||||
exit (1); /* We're scrod. */
|
|
||||||
free_cwd (&cwd);
|
|
||||||
errno = save_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Figure out which device file or directory POINT is mounted on
|
|
||||||
and show its disk usage.
|
|
||||||
STATP is the results of `stat' on POINT. */
|
|
||||||
static void
|
|
||||||
show_point (const char *point, const struct stat *statp)
|
|
||||||
{
|
|
||||||
struct stat disk_stats;
|
|
||||||
struct mount_entry *me;
|
|
||||||
struct mount_entry *matching_dummy = NULL;
|
|
||||||
char *needs_freeing = NULL;
|
|
||||||
|
|
||||||
/* If POINT is an absolute path name, see if we can find the
|
|
||||||
mount point without performing any extra stat calls at all. */
|
|
||||||
if (*point == '/')
|
|
||||||
{
|
|
||||||
for (me = mount_list; me; me = me->me_next)
|
|
||||||
{
|
|
||||||
if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs"))
|
|
||||||
{
|
|
||||||
/* Prefer non-dummy entries. */
|
|
||||||
if (! me->me_dummy)
|
|
||||||
goto show_me;
|
|
||||||
matching_dummy = me;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matching_dummy)
|
|
||||||
goto show_matching_dummy;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_REALPATH || HAVE_RESOLVEPATH
|
|
||||||
/* Calculate the real absolute path for POINT, and use that to find
|
|
||||||
the mount point. This avoids statting unavailable mount points,
|
|
||||||
which can hang df. */
|
|
||||||
{
|
|
||||||
char const *abspoint = point;
|
|
||||||
char *resolved;
|
|
||||||
ssize_t resolved_len;
|
|
||||||
struct mount_entry *best_match = NULL;
|
|
||||||
|
|
||||||
# if HAVE_RESOLVEPATH
|
|
||||||
/* All known hosts with resolvepath (e.g. Solaris 7) don't turn
|
|
||||||
relative names into absolute ones, so prepend the working
|
|
||||||
directory if the path is not absolute. */
|
|
||||||
|
|
||||||
if (*point != '/')
|
|
||||||
{
|
|
||||||
static char const *wd;
|
|
||||||
|
|
||||||
if (! wd)
|
|
||||||
{
|
|
||||||
struct stat pwd_stats;
|
|
||||||
struct stat dot_stats;
|
|
||||||
|
|
||||||
/* Use PWD if it is correct; this is usually cheaper than
|
|
||||||
xgetcwd. */
|
|
||||||
wd = getenv ("PWD");
|
|
||||||
if (! (wd
|
|
||||||
&& stat (wd, &pwd_stats) == 0
|
|
||||||
&& stat (".", &dot_stats) == 0
|
|
||||||
&& SAME_INODE (pwd_stats, dot_stats)))
|
|
||||||
wd = xgetcwd ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wd)
|
|
||||||
{
|
|
||||||
needs_freeing = path_concat (wd, point, NULL);
|
|
||||||
if (needs_freeing)
|
|
||||||
abspoint = needs_freeing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# if HAVE_RESOLVEPATH
|
|
||||||
{
|
|
||||||
size_t resolved_size = strlen (abspoint);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
resolved_size = 2 * resolved_size + 1;
|
|
||||||
resolved = alloca (resolved_size);
|
|
||||||
resolved_len = resolvepath (abspoint, resolved, resolved_size);
|
|
||||||
}
|
|
||||||
while (resolved_len == resolved_size);
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
resolved = alloca (PATH_MAX + 1);
|
|
||||||
resolved = (char *) realpath (abspoint, resolved);
|
|
||||||
resolved_len = resolved ? strlen (resolved) : -1;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
if (1 <= resolved_len && resolved[0] == '/')
|
|
||||||
{
|
|
||||||
size_t best_match_len = 0;
|
|
||||||
|
|
||||||
for (me = mount_list; me; me = me->me_next)
|
|
||||||
if (! me->me_dummy)
|
|
||||||
{
|
|
||||||
size_t len = strlen (me->me_mountdir);
|
|
||||||
if (best_match_len < len && len <= resolved_len
|
|
||||||
&& (len == 1 /* root file system */
|
|
||||||
|| ((len == resolved_len || resolved[len] == '/')
|
|
||||||
&& strncmp (me->me_mountdir, resolved, len) == 0)))
|
|
||||||
{
|
|
||||||
best_match = me;
|
|
||||||
best_match_len = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (best_match && !STREQ (best_match->me_type, "lofs")
|
|
||||||
&& stat (best_match->me_mountdir, &disk_stats) == 0
|
|
||||||
&& disk_stats.st_dev == statp->st_dev)
|
|
||||||
{
|
|
||||||
me = best_match;
|
|
||||||
goto show_me;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (me = mount_list; me; me = me->me_next)
|
|
||||||
{
|
|
||||||
if (me->me_dev == (dev_t) -1)
|
|
||||||
{
|
|
||||||
if (stat (me->me_mountdir, &disk_stats) == 0)
|
|
||||||
me->me_dev = disk_stats.st_dev;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (me->me_mountdir));
|
|
||||||
exit_status = 1;
|
|
||||||
/* So we won't try and fail repeatedly. */
|
|
||||||
me->me_dev = (dev_t) -2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statp->st_dev == me->me_dev)
|
|
||||||
{
|
|
||||||
/* Skip bogus mtab entries. */
|
|
||||||
if (stat (me->me_mountdir, &disk_stats) != 0
|
|
||||||
|| disk_stats.st_dev != me->me_dev)
|
|
||||||
{
|
|
||||||
me->me_dev = (dev_t) -2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prefer non-dummy entries. */
|
|
||||||
if (! me->me_dummy)
|
|
||||||
goto show_me;
|
|
||||||
matching_dummy = me;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matching_dummy)
|
|
||||||
goto show_matching_dummy;
|
|
||||||
|
|
||||||
/* We couldn't find the mount entry corresponding to POINT. Go ahead and
|
|
||||||
print as much info as we can; methods that require the device to be
|
|
||||||
present will fail at a later point. */
|
|
||||||
{
|
|
||||||
/* Find the actual mount point. */
|
|
||||||
char *mp = find_mount_point (point, statp);
|
|
||||||
if (mp)
|
|
||||||
{
|
|
||||||
show_dev (0, mp, 0, 0, 0);
|
|
||||||
free (mp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error (0, errno, "%s", quote (point));
|
|
||||||
}
|
|
||||||
|
|
||||||
goto free_then_return;
|
|
||||||
|
|
||||||
show_matching_dummy:
|
|
||||||
me = matching_dummy;
|
|
||||||
show_me:
|
|
||||||
show_dev (me->me_devname, me->me_mountdir, me->me_type, me->me_dummy,
|
|
||||||
me->me_remote);
|
|
||||||
free_then_return:
|
|
||||||
if (needs_freeing)
|
|
||||||
free (needs_freeing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine what kind of node PATH is and show the disk usage
|
|
||||||
for it. STATP is the results of `stat' on PATH. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
show_entry (const char *path, const struct stat *statp)
|
|
||||||
{
|
|
||||||
if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
|
|
||||||
show_disk (path);
|
|
||||||
else
|
|
||||||
show_point (path, statp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show all mounted filesystems, except perhaps those that are of
|
|
||||||
an unselected type or are empty. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
show_all_entries (void)
|
|
||||||
{
|
|
||||||
struct mount_entry *me;
|
|
||||||
|
|
||||||
for (me = mount_list; me; me = me->me_next)
|
|
||||||
show_dev (me->me_devname, me->me_mountdir, me->me_type,
|
|
||||||
me->me_dummy, me->me_remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add FSTYPE to the list of filesystem types to display. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_fs_type (const char *fstype)
|
|
||||||
{
|
|
||||||
struct fs_type_list *fsp;
|
|
||||||
|
|
||||||
fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
|
|
||||||
fsp->fs_name = (char *) fstype;
|
|
||||||
fsp->fs_next = fs_select_list;
|
|
||||||
fs_select_list = fsp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add FSTYPE to the list of filesystem types to be omitted. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_excluded_fs_type (const char *fstype)
|
|
||||||
{
|
|
||||||
struct fs_type_list *fsp;
|
|
||||||
|
|
||||||
fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
|
|
||||||
fsp->fs_name = (char *) fstype;
|
|
||||||
fsp->fs_next = fs_exclude_list;
|
|
||||||
fs_exclude_list = fsp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Show information about the filesystem on which each FILE resides,\n\
|
|
||||||
or all filesystems by default.\n\
|
|
||||||
\n\
|
|
||||||
-a, --all include filesystems having 0 blocks\n\
|
|
||||||
--block-size=SIZE use SIZE-byte blocks\n\
|
|
||||||
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
|
|
||||||
-H, --si likewise, but use powers of 1000 not 1024\n\
|
|
||||||
-i, --inodes list inode information instead of block usage\n\
|
|
||||||
-k, --kilobytes like --block-size=1024\n\
|
|
||||||
-l, --local limit listing to local filesystems\n\
|
|
||||||
-m, --megabytes like --block-size=1048576\n\
|
|
||||||
--no-sync do not invoke sync before getting usage info (default)\n\
|
|
||||||
-P, --portability use the POSIX output format\n\
|
|
||||||
--sync invoke sync before getting usage info\n\
|
|
||||||
-t, --type=TYPE limit listing to filesystems of type TYPE\n\
|
|
||||||
-T, --print-type print filesystem type\n\
|
|
||||||
-x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
|
|
||||||
-v (ignored)\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
struct stat *stats IF_LINT (= 0);
|
|
||||||
int n_valid_args = 0;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
fs_select_list = NULL;
|
|
||||||
fs_exclude_list = NULL;
|
|
||||||
inode_format = 0;
|
|
||||||
show_all_fs = 0;
|
|
||||||
show_listed_fs = 0;
|
|
||||||
|
|
||||||
human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
|
|
||||||
|
|
||||||
print_type = 0;
|
|
||||||
posix_format = 0;
|
|
||||||
exit_status = 0;
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
|
|
||||||
!= -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0: /* Long option. */
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
show_all_fs = 1;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
inode_format = 1;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
output_block_size = -1024;
|
|
||||||
break;
|
|
||||||
case 'H':
|
|
||||||
output_block_size = -1000;
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
output_block_size = 1024;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
show_local_fs = 1;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
output_block_size = 1024 * 1024;
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
print_type = 1;
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
posix_format = 1;
|
|
||||||
break;
|
|
||||||
case SYNC_OPTION:
|
|
||||||
require_sync = 1;
|
|
||||||
break;
|
|
||||||
case NO_SYNC_OPTION:
|
|
||||||
require_sync = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BLOCK_SIZE_OPTION:
|
|
||||||
human_block_size (optarg, 1, &output_block_size);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'F':
|
|
||||||
/* Accept -F as a synonym for -t for compatibility with Solaris. */
|
|
||||||
case 't':
|
|
||||||
add_fs_type (optarg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'v': /* For SysV compatibility. */
|
|
||||||
/* ignore */
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
add_excluded_fs_type (optarg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fail if the same file system type was both selected and excluded. */
|
|
||||||
{
|
|
||||||
int match = 0;
|
|
||||||
struct fs_type_list *fs_incl;
|
|
||||||
for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
|
|
||||||
{
|
|
||||||
struct fs_type_list *fs_excl;
|
|
||||||
for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
|
|
||||||
{
|
|
||||||
if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("file system type %s both selected and excluded"),
|
|
||||||
quote (fs_incl->fs_name));
|
|
||||||
match = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match)
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* stat all the given entries to make sure they get automounted,
|
|
||||||
if necessary, before reading the filesystem table. */
|
|
||||||
stats = (struct stat *)
|
|
||||||
xmalloc ((argc - optind) * sizeof (struct stat));
|
|
||||||
for (i = optind; i < argc; ++i)
|
|
||||||
{
|
|
||||||
if (stat (argv[i], &stats[i - optind]))
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (argv[i]));
|
|
||||||
exit_status = 1;
|
|
||||||
argv[i] = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++n_valid_args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mount_list =
|
|
||||||
read_filesystem_list ((fs_select_list != NULL
|
|
||||||
|| fs_exclude_list != NULL
|
|
||||||
|| print_type
|
|
||||||
|| show_local_fs));
|
|
||||||
|
|
||||||
if (mount_list == NULL)
|
|
||||||
{
|
|
||||||
/* Couldn't read the table of mounted filesystems.
|
|
||||||
Fail if df was invoked with no file name arguments;
|
|
||||||
Otherwise, merely give a warning and proceed. */
|
|
||||||
const char *warning = (optind == argc ? "" : _("Warning: "));
|
|
||||||
int status = (optind == argc ? 1 : 0);
|
|
||||||
error (status, errno,
|
|
||||||
_("%scannot read table of mounted filesystems"), warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require_sync)
|
|
||||||
sync ();
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
print_header ();
|
|
||||||
show_all_entries ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Display explicitly requested empty filesystems. */
|
|
||||||
show_listed_fs = 1;
|
|
||||||
|
|
||||||
if (n_valid_args > 0)
|
|
||||||
print_header ();
|
|
||||||
|
|
||||||
for (i = optind; i < argc; ++i)
|
|
||||||
if (argv[i])
|
|
||||||
show_entry (argv[i], &stats[i - optind]);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (exit_status);
|
|
||||||
}
|
|
||||||
@@ -1,794 +0,0 @@
|
|||||||
/* du -- summarize disk usage
|
|
||||||
Copyright (C) 88, 89, 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Differences from the Unix du:
|
|
||||||
* Doesn't simply ignore the names of regular files given as arguments
|
|
||||||
when -a is given.
|
|
||||||
* Additional options:
|
|
||||||
-l Count the size of all files, even if they have appeared
|
|
||||||
already in another hard link.
|
|
||||||
-x Do not cross file-system boundaries during the recursion.
|
|
||||||
-c Write a grand total of all of the arguments after all
|
|
||||||
arguments have been processed. This can be used to find
|
|
||||||
out the disk usage of a directory, with some files excluded.
|
|
||||||
-h Print sizes in human readable format (1k 234M 2G, etc).
|
|
||||||
-H Similar, but use powers of 1000 not 1024.
|
|
||||||
-k Print sizes in kilobytes.
|
|
||||||
-m Print sizes in megabytes.
|
|
||||||
-b Print sizes in bytes.
|
|
||||||
-S Count the size of each directory separately, not including
|
|
||||||
the sizes of subdirectories.
|
|
||||||
-D Dereference only symbolic links given on the command line.
|
|
||||||
-L Dereference all symbolic links.
|
|
||||||
--exclude=PAT Exclude files that match PAT.
|
|
||||||
-X FILE Exclude files that match patterns taken from FILE.
|
|
||||||
|
|
||||||
By [email protected], Torbjorn Granlund,
|
|
||||||
and [email protected], David MacKenzie.
|
|
||||||
Variable blocks added by [email protected] and [email protected].
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#if HAVE_INTTYPES_H
|
|
||||||
# include <inttypes.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "exclude.h"
|
|
||||||
#include "human.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "save-cwd.h"
|
|
||||||
#include "savedir.h"
|
|
||||||
#include "xstrtol.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "du"
|
|
||||||
|
|
||||||
#define AUTHORS \
|
|
||||||
"Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
|
|
||||||
|
|
||||||
/* Initial number of entries in each hash table entry's table of inodes. */
|
|
||||||
#define INITIAL_HASH_MODULE 100
|
|
||||||
|
|
||||||
/* Initial number of entries in the inode hash table. */
|
|
||||||
#define INITIAL_ENTRY_TAB_SIZE 70
|
|
||||||
|
|
||||||
/* Initial size to allocate for `path'. */
|
|
||||||
#define INITIAL_PATH_SIZE 100
|
|
||||||
|
|
||||||
/* Hash structure for inode and device numbers. The separate entry
|
|
||||||
structure makes it easier to rehash "in place". */
|
|
||||||
|
|
||||||
struct entry
|
|
||||||
{
|
|
||||||
ino_t ino;
|
|
||||||
dev_t dev;
|
|
||||||
struct entry *coll_link;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Structure for a hash table for inode numbers. */
|
|
||||||
|
|
||||||
struct htab
|
|
||||||
{
|
|
||||||
unsigned modulus; /* Size of the `hash' pointer vector. */
|
|
||||||
struct entry *entry_tab; /* Pointer to dynamically growing vector. */
|
|
||||||
unsigned entry_tab_size; /* Size of current `entry_tab' allocation. */
|
|
||||||
unsigned first_free_entry; /* Index in `entry_tab'. */
|
|
||||||
struct entry *hash[1]; /* Vector of pointers in `entry_tab'. */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Structure for dynamically resizable strings. */
|
|
||||||
|
|
||||||
struct String
|
|
||||||
{
|
|
||||||
unsigned alloc; /* Size of allocation for the text. */
|
|
||||||
unsigned length; /* Length of the text currently. */
|
|
||||||
char *text; /* Pointer to the text. */
|
|
||||||
};
|
|
||||||
typedef struct String String;
|
|
||||||
|
|
||||||
int stat ();
|
|
||||||
int lstat ();
|
|
||||||
|
|
||||||
/* Name under which this program was invoked. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, display counts for all files, not just directories. */
|
|
||||||
static int opt_all = 0;
|
|
||||||
|
|
||||||
/* If nonzero, count each hard link of files with multiple links. */
|
|
||||||
static int opt_count_all = 0;
|
|
||||||
|
|
||||||
/* If nonzero, do not cross file-system boundaries. */
|
|
||||||
static int opt_one_file_system = 0;
|
|
||||||
|
|
||||||
/* If nonzero, print a grand total at the end. */
|
|
||||||
static int print_totals = 0;
|
|
||||||
|
|
||||||
/* If nonzero, do not add sizes of subdirectories. */
|
|
||||||
static int opt_separate_dirs = 0;
|
|
||||||
|
|
||||||
/* If nonzero, dereference symlinks that are command line arguments. */
|
|
||||||
static int opt_dereference_arguments = 0;
|
|
||||||
|
|
||||||
/* Show the total for each directory (and file if --all) that is at
|
|
||||||
most MAX_DEPTH levels down from the root of the hierarchy. The root
|
|
||||||
is at level 0, so `du --max-depth=0' is equivalent to `du -s'. */
|
|
||||||
static int max_depth = INT_MAX;
|
|
||||||
|
|
||||||
/* If positive, the units to use when printing sizes;
|
|
||||||
if negative, the human-readable base. */
|
|
||||||
static int output_block_size;
|
|
||||||
|
|
||||||
/* Accumulated path for file or directory being processed. */
|
|
||||||
static String *path;
|
|
||||||
|
|
||||||
/* Pointer to hash structure, used by the hash routines. */
|
|
||||||
static struct htab *htab;
|
|
||||||
|
|
||||||
/* A pointer to either lstat or stat, depending on whether
|
|
||||||
dereferencing of all symbolic links is to be done. */
|
|
||||||
static int (*xstat) ();
|
|
||||||
|
|
||||||
/* The exit status to use if we don't get any fatal errors. */
|
|
||||||
static int exit_status;
|
|
||||||
|
|
||||||
/* File name patterns to exclude. */
|
|
||||||
static struct exclude *exclude;
|
|
||||||
|
|
||||||
/* Grand total size of all args, in units of ST_NBLOCKSIZE-byte blocks. */
|
|
||||||
static uintmax_t tot_size = 0;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
EXCLUDE_OPTION = CHAR_MAX + 1,
|
|
||||||
BLOCK_SIZE_OPTION,
|
|
||||||
MAX_DEPTH_OPTION
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"all", no_argument, NULL, 'a'},
|
|
||||||
{"block-size", required_argument, 0, BLOCK_SIZE_OPTION},
|
|
||||||
{"bytes", no_argument, NULL, 'b'},
|
|
||||||
{"count-links", no_argument, NULL, 'l'},
|
|
||||||
{"dereference", no_argument, NULL, 'L'},
|
|
||||||
{"dereference-args", no_argument, NULL, 'D'},
|
|
||||||
{"exclude", required_argument, 0, EXCLUDE_OPTION},
|
|
||||||
{"exclude-from", required_argument, 0, 'X'},
|
|
||||||
{"human-readable", no_argument, NULL, 'h'},
|
|
||||||
{"si", no_argument, 0, 'H'},
|
|
||||||
{"kilobytes", no_argument, NULL, 'k'},
|
|
||||||
{"max-depth", required_argument, NULL, MAX_DEPTH_OPTION},
|
|
||||||
{"megabytes", no_argument, NULL, 'm'},
|
|
||||||
{"one-file-system", no_argument, NULL, 'x'},
|
|
||||||
{"separate-dirs", no_argument, NULL, 'S'},
|
|
||||||
{"summarize", no_argument, NULL, 's'},
|
|
||||||
{"total", no_argument, NULL, 'c'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Summarize disk usage of each FILE, recursively for directories.\n\
|
|
||||||
\n\
|
|
||||||
-a, --all write counts for all files, not just directories\n\
|
|
||||||
--block-size=SIZE use SIZE-byte blocks\n\
|
|
||||||
-b, --bytes print size in bytes\n\
|
|
||||||
-c, --total produce a grand total\n\
|
|
||||||
-D, --dereference-args dereference PATHs when symbolic link\n\
|
|
||||||
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
|
|
||||||
-H, --si likewise, but use powers of 1000 not 1024\n\
|
|
||||||
-k, --kilobytes like --block-size=1024\n\
|
|
||||||
-l, --count-links count sizes many times if hard linked\n\
|
|
||||||
-L, --dereference dereference all symbolic links\n\
|
|
||||||
-m, --megabytes like --block-size=1048576\n\
|
|
||||||
-S, --separate-dirs do not include size of subdirectories\n\
|
|
||||||
-s, --summarize display only a total for each argument\n\
|
|
||||||
-x, --one-file-system skip directories on different filesystems\n\
|
|
||||||
-X FILE, --exclude-from=FILE Exclude files that match any pattern in FILE.\n\
|
|
||||||
--exclude=PAT Exclude files that match PAT.\n\
|
|
||||||
--max-depth=N print the total for a directory (or file, with --all)\n\
|
|
||||||
only if it is N or fewer levels below the command\n\
|
|
||||||
line argument; --max-depth=0 is the same as\n\
|
|
||||||
--summarize\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize string S1 to hold SIZE characters. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
str_init (String **s1, unsigned int size)
|
|
||||||
{
|
|
||||||
String *s;
|
|
||||||
|
|
||||||
s = (String *) xmalloc (sizeof (struct String));
|
|
||||||
s->text = xmalloc (size + 1);
|
|
||||||
|
|
||||||
s->alloc = size;
|
|
||||||
*s1 = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ensure_space (String *s, unsigned int size)
|
|
||||||
{
|
|
||||||
if (s->alloc < size)
|
|
||||||
{
|
|
||||||
s->text = xrealloc (s->text, size + 1);
|
|
||||||
s->alloc = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assign the null-terminated C-string CSTR to S1. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
str_copyc (String *s1, const char *cstr)
|
|
||||||
{
|
|
||||||
unsigned l = strlen (cstr);
|
|
||||||
ensure_space (s1, l);
|
|
||||||
strcpy (s1->text, cstr);
|
|
||||||
s1->length = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
str_concatc (String *s1, const char *cstr)
|
|
||||||
{
|
|
||||||
unsigned l1 = s1->length;
|
|
||||||
unsigned l2 = strlen (cstr);
|
|
||||||
unsigned l = l1 + l2;
|
|
||||||
|
|
||||||
ensure_space (s1, l);
|
|
||||||
strcpy (s1->text + l1, cstr);
|
|
||||||
s1->length = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Truncate the string S1 to have length LENGTH. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
str_trunc (String *s1, unsigned int length)
|
|
||||||
{
|
|
||||||
if (s1->length > length)
|
|
||||||
{
|
|
||||||
s1->text[length] = 0;
|
|
||||||
s1->length = length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print N_BLOCKS followed by STRING on a line. NBLOCKS is the number of
|
|
||||||
ST_NBLOCKSIZE-byte blocks; convert it to OUTPUT_BLOCK_SIZE units before
|
|
||||||
printing. If OUTPUT_BLOCK_SIZE is negative, use a human readable
|
|
||||||
notation instead. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_size (uintmax_t n_blocks, const char *string)
|
|
||||||
{
|
|
||||||
char buf[LONGEST_HUMAN_READABLE + 1];
|
|
||||||
printf ("%s\t%s\n",
|
|
||||||
human_readable_inexact (n_blocks, buf, ST_NBLOCKSIZE,
|
|
||||||
output_block_size, human_ceiling),
|
|
||||||
string);
|
|
||||||
fflush (stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset the hash structure in the global variable `htab' to
|
|
||||||
contain no entries. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
hash_reset (void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct entry **p;
|
|
||||||
|
|
||||||
htab->first_free_entry = 0;
|
|
||||||
|
|
||||||
p = htab->hash;
|
|
||||||
for (i = htab->modulus; i > 0; i--)
|
|
||||||
*p++ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate space for the hash structures, and set the global
|
|
||||||
variable `htab' to point to it. The initial hash module is specified in
|
|
||||||
MODULUS, and the number of entries are specified in ENTRY_TAB_SIZE. (The
|
|
||||||
hash structure will be rebuilt when ENTRY_TAB_SIZE entries have been
|
|
||||||
inserted, and MODULUS and ENTRY_TAB_SIZE in the global `htab' will be
|
|
||||||
doubled.) */
|
|
||||||
|
|
||||||
static void
|
|
||||||
hash_init (unsigned int modulus, unsigned int entry_tab_size)
|
|
||||||
{
|
|
||||||
struct htab *htab_r;
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xmalloc (sizeof (struct htab) + sizeof (struct entry *) * modulus);
|
|
||||||
|
|
||||||
htab_r->entry_tab = (struct entry *)
|
|
||||||
xmalloc (sizeof (struct entry) * entry_tab_size);
|
|
||||||
|
|
||||||
htab_r->modulus = modulus;
|
|
||||||
htab_r->entry_tab_size = entry_tab_size;
|
|
||||||
htab = htab_r;
|
|
||||||
|
|
||||||
hash_reset ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert INO and DEV in the hash structure HTAB, if not
|
|
||||||
already present. Return zero if inserted and nonzero if it
|
|
||||||
already existed. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_insert2 (struct htab *ht, ino_t ino, dev_t dev)
|
|
||||||
{
|
|
||||||
struct entry **hp, *ep2, *ep;
|
|
||||||
hp = &ht->hash[ino % ht->modulus];
|
|
||||||
ep2 = *hp;
|
|
||||||
|
|
||||||
/* Collision? */
|
|
||||||
|
|
||||||
if (ep2 != NULL)
|
|
||||||
{
|
|
||||||
ep = ep2;
|
|
||||||
|
|
||||||
/* Search for an entry with the same data. */
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (ep->ino == ino && ep->dev == dev)
|
|
||||||
return 1; /* Found an entry with the same data. */
|
|
||||||
ep = ep->coll_link;
|
|
||||||
}
|
|
||||||
while (ep != NULL);
|
|
||||||
|
|
||||||
/* Did not find it. */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ep = *hp = &ht->entry_tab[ht->first_free_entry++];
|
|
||||||
ep->ino = ino;
|
|
||||||
ep->dev = dev;
|
|
||||||
ep->coll_link = ep2; /* `ep2' is NULL if no collision. */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert an item (inode INO and device DEV) in the hash
|
|
||||||
structure in the global variable `htab', if an entry with the same data
|
|
||||||
was not found already. Return zero if the item was inserted and nonzero
|
|
||||||
if it wasn't. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_insert (ino_t ino, dev_t dev)
|
|
||||||
{
|
|
||||||
struct htab *htab_r = htab; /* Initially a copy of the global `htab'. */
|
|
||||||
|
|
||||||
if (htab_r->first_free_entry >= htab_r->entry_tab_size)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct entry *ep;
|
|
||||||
unsigned modulus;
|
|
||||||
unsigned entry_tab_size;
|
|
||||||
|
|
||||||
/* Increase the number of hash entries, and re-hash the data.
|
|
||||||
The method of shrimping and increasing is made to compactify
|
|
||||||
the heap. If twice as much data would be allocated
|
|
||||||
straightforwardly, we would never re-use a byte of memory. */
|
|
||||||
|
|
||||||
/* Let `htab' shrimp. Keep only the header, not the pointer vector. */
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xrealloc ((char *) htab_r, sizeof (struct htab));
|
|
||||||
|
|
||||||
modulus = 2 * htab_r->modulus;
|
|
||||||
entry_tab_size = 2 * htab_r->entry_tab_size;
|
|
||||||
|
|
||||||
/* Increase the number of possible entries. */
|
|
||||||
|
|
||||||
htab_r->entry_tab = (struct entry *)
|
|
||||||
xrealloc ((char *) htab_r->entry_tab,
|
|
||||||
sizeof (struct entry) * entry_tab_size);
|
|
||||||
|
|
||||||
/* Increase the size of htab again. */
|
|
||||||
|
|
||||||
htab_r = (struct htab *)
|
|
||||||
xrealloc ((char *) htab_r,
|
|
||||||
sizeof (struct htab) + sizeof (struct entry *) * modulus);
|
|
||||||
|
|
||||||
htab_r->modulus = modulus;
|
|
||||||
htab_r->entry_tab_size = entry_tab_size;
|
|
||||||
htab = htab_r;
|
|
||||||
|
|
||||||
i = htab_r->first_free_entry;
|
|
||||||
|
|
||||||
/* Make the increased hash table empty. The entries are still
|
|
||||||
available in htab->entry_tab. */
|
|
||||||
|
|
||||||
hash_reset ();
|
|
||||||
|
|
||||||
/* Go through the entries and install them in the pointer vector
|
|
||||||
htab->hash. The items are actually inserted in htab->entry_tab at
|
|
||||||
the position where they already are. The htab->coll_link need
|
|
||||||
however be updated. Could be made a little more efficient. */
|
|
||||||
|
|
||||||
for (ep = htab_r->entry_tab; i > 0; i--)
|
|
||||||
{
|
|
||||||
hash_insert2 (htab_r, ep->ino, ep->dev);
|
|
||||||
ep++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash_insert2 (htab_r, ino, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore the previous working directory or exit.
|
|
||||||
If CWD is null, simply call `chdir ("..")'. Otherwise,
|
|
||||||
use CWD and free it. CURR_DIR_NAME is the name of the current directory
|
|
||||||
and is used solely in failure diagnostics. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
pop_dir (struct saved_cwd *cwd, const char *curr_dir_name)
|
|
||||||
{
|
|
||||||
if (cwd)
|
|
||||||
{
|
|
||||||
if (restore_cwd (cwd, "..", curr_dir_name))
|
|
||||||
exit (1);
|
|
||||||
free_cwd (cwd);
|
|
||||||
}
|
|
||||||
else if (chdir ("..") < 0)
|
|
||||||
{
|
|
||||||
error (1, errno, _("cannot change to `..' from directory %s"),
|
|
||||||
quote (curr_dir_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print (if appropriate) the size (in units determined by `output_block_size')
|
|
||||||
of file or directory ENT. Return the size of ENT in units of 512-byte
|
|
||||||
blocks. TOP is one for external calls, zero for recursive calls.
|
|
||||||
LAST_DEV is the device that the parent directory of ENT is on.
|
|
||||||
DEPTH is the number of levels (in hierarchy) down from a command
|
|
||||||
line argument. Don't print if DEPTH > max_depth.
|
|
||||||
An important invariant is that when this function returns, the current
|
|
||||||
working directory is the same as when it was called. */
|
|
||||||
|
|
||||||
static uintmax_t
|
|
||||||
count_entry (const char *ent, int top, dev_t last_dev, int depth)
|
|
||||||
{
|
|
||||||
uintmax_t size;
|
|
||||||
struct stat stat_buf;
|
|
||||||
|
|
||||||
if (((top && opt_dereference_arguments)
|
|
||||||
? stat (ent, &stat_buf)
|
|
||||||
: (*xstat) (ent, &stat_buf)) < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (path->text));
|
|
||||||
exit_status = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!opt_count_all
|
|
||||||
&& stat_buf.st_nlink > 1
|
|
||||||
&& hash_insert (stat_buf.st_ino, stat_buf.st_dev))
|
|
||||||
return 0; /* Have counted this already. */
|
|
||||||
|
|
||||||
size = ST_NBLOCKS (stat_buf);
|
|
||||||
tot_size += size;
|
|
||||||
|
|
||||||
if (S_ISDIR (stat_buf.st_mode))
|
|
||||||
{
|
|
||||||
unsigned pathlen;
|
|
||||||
dev_t dir_dev;
|
|
||||||
char *name_space;
|
|
||||||
char *namep;
|
|
||||||
struct saved_cwd *cwd;
|
|
||||||
struct saved_cwd cwd_buf;
|
|
||||||
struct stat e_buf;
|
|
||||||
|
|
||||||
dir_dev = stat_buf.st_dev;
|
|
||||||
|
|
||||||
/* Return `0' here, not SIZE, since the SIZE bytes
|
|
||||||
would reside in the new filesystem. */
|
|
||||||
if (opt_one_file_system && !top && last_dev != dir_dev)
|
|
||||||
return 0; /* Don't enter a new file system. */
|
|
||||||
|
|
||||||
#ifndef S_ISLNK
|
|
||||||
# define S_ISLNK(s) 0
|
|
||||||
#endif
|
|
||||||
/* If we're traversing more than one level, or if we're
|
|
||||||
dereferencing symlinks and we're about to chdir through a
|
|
||||||
symlink, remember the current directory so we can return to
|
|
||||||
it later. In other cases, chdir ("..") works fine.
|
|
||||||
Treat `.' and `..' like multi-level paths, since `chdir ("..")'
|
|
||||||
wont't restore the current working directory after a `chdir'
|
|
||||||
to one of those. */
|
|
||||||
if (strchr (ent, '/')
|
|
||||||
|| DOT_OR_DOTDOT (ent)
|
|
||||||
|| (xstat == stat
|
|
||||||
&& lstat (ent, &e_buf) == 0
|
|
||||||
&& S_ISLNK (e_buf.st_mode)))
|
|
||||||
{
|
|
||||||
if (save_cwd (&cwd_buf))
|
|
||||||
exit (1);
|
|
||||||
cwd = &cwd_buf;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cwd = NULL;
|
|
||||||
|
|
||||||
if (chdir (ent) < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot change to directory %s"),
|
|
||||||
quote (path->text));
|
|
||||||
if (cwd)
|
|
||||||
free_cwd (cwd);
|
|
||||||
exit_status = 1;
|
|
||||||
/* Do return SIZE, here, since even though we can't chdir into ENT,
|
|
||||||
we *can* count the blocks used by its directory entry. */
|
|
||||||
return opt_separate_dirs ? 0 : size;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_space = savedir (".", stat_buf.st_size);
|
|
||||||
if (name_space == NULL)
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (path->text));
|
|
||||||
pop_dir (cwd, path->text);
|
|
||||||
exit_status = 1;
|
|
||||||
/* Do count the SIZE bytes. */
|
|
||||||
return opt_separate_dirs ? 0 : size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember the current path. */
|
|
||||||
|
|
||||||
str_concatc (path, "/");
|
|
||||||
pathlen = path->length;
|
|
||||||
|
|
||||||
for (namep = name_space; *namep; namep += strlen (namep) + 1)
|
|
||||||
{
|
|
||||||
if (!excluded_filename (exclude, namep, 0))
|
|
||||||
{
|
|
||||||
str_concatc (path, namep);
|
|
||||||
size += count_entry (namep, 0, dir_dev, depth + 1);
|
|
||||||
str_trunc (path, pathlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free (name_space);
|
|
||||||
pop_dir (cwd, path->text);
|
|
||||||
|
|
||||||
str_trunc (path, pathlen - 1); /* Remove the "/" we added. */
|
|
||||||
if (depth <= max_depth || top)
|
|
||||||
print_size (size, path->length > 0 ? path->text : "/");
|
|
||||||
return opt_separate_dirs ? 0 : size;
|
|
||||||
}
|
|
||||||
else if ((opt_all && depth <= max_depth) || top)
|
|
||||||
{
|
|
||||||
/* FIXME: make this an option. */
|
|
||||||
int print_only_dir_size = 0;
|
|
||||||
if (!print_only_dir_size)
|
|
||||||
print_size (size, path->length > 0 ? path->text : "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively print the sizes of the directories (and, if selected, files)
|
|
||||||
named in FILES, the last entry of which is NULL. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
du_files (char **files)
|
|
||||||
{
|
|
||||||
int i; /* Index in FILES. */
|
|
||||||
|
|
||||||
for (i = 0; files[i]; i++)
|
|
||||||
{
|
|
||||||
char *arg;
|
|
||||||
int s;
|
|
||||||
|
|
||||||
arg = files[i];
|
|
||||||
|
|
||||||
/* Delete final slash in the argument, unless the slash is alone. */
|
|
||||||
s = strlen (arg) - 1;
|
|
||||||
if (s != 0)
|
|
||||||
{
|
|
||||||
if (arg[s] == '/')
|
|
||||||
arg[s] = 0;
|
|
||||||
|
|
||||||
str_copyc (path, arg);
|
|
||||||
}
|
|
||||||
else if (arg[0] == '/')
|
|
||||||
str_trunc (path, 0); /* Null path for root directory. */
|
|
||||||
else
|
|
||||||
str_copyc (path, arg);
|
|
||||||
|
|
||||||
if (!print_totals)
|
|
||||||
hash_reset ();
|
|
||||||
|
|
||||||
count_entry (arg, 1, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_totals)
|
|
||||||
print_size (tot_size, _("total"));
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
char *cwd_only[2];
|
|
||||||
int max_depth_specified = 0;
|
|
||||||
|
|
||||||
/* If nonzero, display only a total for each argument. */
|
|
||||||
int opt_summarize_only = 0;
|
|
||||||
|
|
||||||
cwd_only[0] = ".";
|
|
||||||
cwd_only[1] = NULL;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
exclude = new_exclude ();
|
|
||||||
xstat = lstat;
|
|
||||||
|
|
||||||
human_block_size (getenv ("DU_BLOCK_SIZE"), 0, &output_block_size);
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "abchHklmsxDLSX:", long_options, NULL))
|
|
||||||
!= -1)
|
|
||||||
{
|
|
||||||
long int tmp_long;
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0: /* Long option. */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
opt_all = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
output_block_size = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
print_totals = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
output_block_size = -1024;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'H':
|
|
||||||
output_block_size = -1000;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'k':
|
|
||||||
output_block_size = 1024;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MAX_DEPTH_OPTION: /* --max-depth=N */
|
|
||||||
if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
|
|
||||||
|| tmp_long < 0 || tmp_long > INT_MAX)
|
|
||||||
error (1, 0, _("invalid maximum depth %s"), quote (optarg));
|
|
||||||
|
|
||||||
max_depth_specified = 1;
|
|
||||||
max_depth = (int) tmp_long;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'm':
|
|
||||||
output_block_size = 1024 * 1024;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
opt_count_all = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
opt_summarize_only = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
opt_one_file_system = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'D':
|
|
||||||
opt_dereference_arguments = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
xstat = stat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'S':
|
|
||||||
opt_separate_dirs = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'X':
|
|
||||||
if (add_exclude_file (add_exclude, exclude, optarg, '\n') != 0)
|
|
||||||
error (1, errno, "%s", quote (optarg));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EXCLUDE_OPTION:
|
|
||||||
add_exclude (exclude, optarg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BLOCK_SIZE_OPTION:
|
|
||||||
human_block_size (optarg, 1, &output_block_size);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_all && opt_summarize_only)
|
|
||||||
{
|
|
||||||
error (0, 0, _("cannot both summarize and show all entries"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_summarize_only && max_depth_specified && max_depth == 0)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: summarizing is the same as using --max-depth=0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_summarize_only && max_depth_specified && max_depth != 0)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: summarizing conflicts with --max-depth=%d"),
|
|
||||||
max_depth);
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_summarize_only)
|
|
||||||
max_depth = 0;
|
|
||||||
|
|
||||||
/* Initialize the hash structure for inode numbers. */
|
|
||||||
hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
|
|
||||||
|
|
||||||
str_init (&path, INITIAL_PATH_SIZE);
|
|
||||||
|
|
||||||
du_files (optind == argc ? cwd_only : argv + optind);
|
|
||||||
|
|
||||||
exit (exit_status);
|
|
||||||
}
|
|
||||||
@@ -1,643 +0,0 @@
|
|||||||
/* install - copy files and set attributes
|
|
||||||
Copyright (C) 89, 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <grp.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "backupfile.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "cp-hash.h"
|
|
||||||
#include "copy.h"
|
|
||||||
#include "dirname.h"
|
|
||||||
#include "makepath.h"
|
|
||||||
#include "modechange.h"
|
|
||||||
#include "path-concat.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "xstrtol.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "install"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
#if HAVE_SYS_WAIT_H
|
|
||||||
# include <sys/wait.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_VALUES_H
|
|
||||||
# include <values.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct passwd *getpwnam ();
|
|
||||||
struct group *getgrnam ();
|
|
||||||
|
|
||||||
#ifndef _POSIX_VERSION
|
|
||||||
uid_t getuid ();
|
|
||||||
gid_t getgid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ! HAVE_ENDGRENT
|
|
||||||
# define endgrent() ((void) 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ! HAVE_ENDPWENT
|
|
||||||
# define endpwent() ((void) 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initial number of entries in each hash table entry's table of inodes. */
|
|
||||||
#define INITIAL_HASH_MODULE 100
|
|
||||||
|
|
||||||
/* Initial number of entries in the inode hash table. */
|
|
||||||
#define INITIAL_ENTRY_TAB_SIZE 70
|
|
||||||
|
|
||||||
/* Number of bytes of a file to copy at a time. */
|
|
||||||
#define READ_SIZE (32 * 1024)
|
|
||||||
|
|
||||||
int full_write ();
|
|
||||||
int isdir ();
|
|
||||||
|
|
||||||
int stat ();
|
|
||||||
|
|
||||||
static int change_timestamps PARAMS ((const char *from, const char *to));
|
|
||||||
static int change_attributes PARAMS ((const char *path));
|
|
||||||
static int copy_file PARAMS ((const char *from, const char *to,
|
|
||||||
const struct cp_options *x));
|
|
||||||
static int install_file_to_path PARAMS ((const char *from, const char *to,
|
|
||||||
const struct cp_options *x));
|
|
||||||
static int install_file_in_dir PARAMS ((const char *from, const char *to_dir,
|
|
||||||
const struct cp_options *x));
|
|
||||||
static int install_file_in_file PARAMS ((const char *from, const char *to,
|
|
||||||
const struct cp_options *x));
|
|
||||||
static void get_ids PARAMS ((void));
|
|
||||||
static void strip PARAMS ((const char *path));
|
|
||||||
void usage PARAMS ((int status));
|
|
||||||
|
|
||||||
/* The name this program was run with, for error messages. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* The user name that will own the files, or NULL to make the owner
|
|
||||||
the current user ID. */
|
|
||||||
static char *owner_name;
|
|
||||||
|
|
||||||
/* The user ID corresponding to `owner_name'. */
|
|
||||||
static uid_t owner_id;
|
|
||||||
|
|
||||||
/* The group name that will own the files, or NULL to make the group
|
|
||||||
the current group ID. */
|
|
||||||
static char *group_name;
|
|
||||||
|
|
||||||
/* The group ID corresponding to `group_name'. */
|
|
||||||
static gid_t group_id;
|
|
||||||
|
|
||||||
/* The permissions to which the files will be set. The umask has
|
|
||||||
no effect. */
|
|
||||||
static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
|
||||||
|
|
||||||
/* If nonzero, strip executable files after copying them. */
|
|
||||||
static int strip_files;
|
|
||||||
|
|
||||||
/* If nonzero, install a directory instead of a regular file. */
|
|
||||||
static int dir_arg;
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"backup", optional_argument, NULL, 'b'},
|
|
||||||
{"directory", no_argument, NULL, 'd'},
|
|
||||||
{"group", required_argument, NULL, 'g'},
|
|
||||||
{"mode", required_argument, NULL, 'm'},
|
|
||||||
{"owner", required_argument, NULL, 'o'},
|
|
||||||
{"preserve-timestamps", no_argument, NULL, 'p'},
|
|
||||||
{"strip", no_argument, NULL, 's'},
|
|
||||||
{"suffix", required_argument, NULL, 'S'},
|
|
||||||
{"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
cp_option_init (struct cp_options *x)
|
|
||||||
{
|
|
||||||
x->copy_as_regular = 1;
|
|
||||||
x->dereference = DEREF_ALWAYS;
|
|
||||||
x->unlink_dest_before_opening = 1;
|
|
||||||
x->unlink_dest_after_failed_open = 0;
|
|
||||||
|
|
||||||
/* If unlink fails, try to proceed anyway. */
|
|
||||||
x->failed_unlink_is_fatal = 0;
|
|
||||||
|
|
||||||
x->hard_link = 0;
|
|
||||||
x->interactive = 0;
|
|
||||||
x->move_mode = 0;
|
|
||||||
x->myeuid = geteuid ();
|
|
||||||
x->one_file_system = 0;
|
|
||||||
x->preserve_owner_and_group = 0;
|
|
||||||
x->preserve_chmod_bits = 0;
|
|
||||||
x->preserve_timestamps = 0;
|
|
||||||
x->require_preserve = 0;
|
|
||||||
x->recursive = 0;
|
|
||||||
x->sparse_mode = SPARSE_AUTO;
|
|
||||||
x->symbolic_link = 0;
|
|
||||||
x->backup_type = none;
|
|
||||||
|
|
||||||
/* Create destination files initially writable so we can run strip on them.
|
|
||||||
Although GNU strip works fine on read-only files, some others
|
|
||||||
would fail. */
|
|
||||||
x->set_mode = 1;
|
|
||||||
x->mode = S_IRUSR | S_IWUSR;
|
|
||||||
|
|
||||||
x->umask_kill = 0;
|
|
||||||
x->update = 0;
|
|
||||||
x->verbose = 0;
|
|
||||||
x->xstat = stat;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int optc;
|
|
||||||
int errors = 0;
|
|
||||||
const char *specified_mode = NULL;
|
|
||||||
int make_backups = 0;
|
|
||||||
char *backup_suffix_string;
|
|
||||||
char *version_control_string = NULL;
|
|
||||||
int mkdir_and_install = 0;
|
|
||||||
struct cp_options x;
|
|
||||||
int n_files;
|
|
||||||
char **file;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
cp_option_init (&x);
|
|
||||||
|
|
||||||
owner_name = NULL;
|
|
||||||
group_name = NULL;
|
|
||||||
strip_files = 0;
|
|
||||||
dir_arg = 0;
|
|
||||||
umask (0);
|
|
||||||
|
|
||||||
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
|
|
||||||
we'll actually use backup_suffix_string. */
|
|
||||||
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pvV:S:", long_options,
|
|
||||||
NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'V': /* FIXME: this is deprecated. Remove it in 2001. */
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: --version-control (-V) is obsolete; support for\
|
|
||||||
it\nwill be removed in some future release. Use --backup=%s instead."
|
|
||||||
), optarg);
|
|
||||||
/* Fall through. */
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
make_backups = 1;
|
|
||||||
if (optarg)
|
|
||||||
version_control_string = optarg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
strip_files = 1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
dir_arg = 1;
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
mkdir_and_install = 1;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
x.verbose = 1;
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
group_name = optarg;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
specified_mode = optarg;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
owner_name = optarg;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
x.preserve_timestamps = 1;
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
make_backups = 1;
|
|
||||||
backup_suffix_string = optarg;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for invalid combinations of arguments. */
|
|
||||||
if (dir_arg && strip_files)
|
|
||||||
error (1, 0,
|
|
||||||
_("the strip option may not be used when installing a directory"));
|
|
||||||
|
|
||||||
if (backup_suffix_string)
|
|
||||||
simple_backup_suffix = xstrdup (backup_suffix_string);
|
|
||||||
|
|
||||||
x.backup_type = (make_backups
|
|
||||||
? xget_version (_("backup type"),
|
|
||||||
version_control_string)
|
|
||||||
: none);
|
|
||||||
|
|
||||||
n_files = argc - optind;
|
|
||||||
file = argv + optind;
|
|
||||||
|
|
||||||
if (n_files == 0 || (n_files == 1 && !dir_arg))
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (specified_mode)
|
|
||||||
{
|
|
||||||
struct mode_change *change = mode_compile (specified_mode, 0);
|
|
||||||
if (change == MODE_INVALID)
|
|
||||||
error (1, 0, _("invalid mode %s"), quote (specified_mode));
|
|
||||||
else if (change == MODE_MEMORY_EXHAUSTED)
|
|
||||||
xalloc_die ();
|
|
||||||
mode = mode_adjust (0, change);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_ids ();
|
|
||||||
|
|
||||||
if (dir_arg)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < n_files; i++)
|
|
||||||
{
|
|
||||||
errors |=
|
|
||||||
make_path (file[i], mode, mode, owner_id, group_id, 0,
|
|
||||||
(x.verbose ? _("creating directory %s") : NULL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* FIXME: it's a little gross that this initialization is
|
|
||||||
required by copy.c::copy. */
|
|
||||||
hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
|
|
||||||
|
|
||||||
if (n_files == 2)
|
|
||||||
{
|
|
||||||
if (mkdir_and_install)
|
|
||||||
errors = install_file_to_path (file[0], file[1], &x);
|
|
||||||
else if (!isdir (file[1]))
|
|
||||||
errors = install_file_in_file (file[0], file[1], &x);
|
|
||||||
else
|
|
||||||
errors = install_file_in_dir (file[0], file[1], &x);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
const char *dest = file[n_files - 1];
|
|
||||||
if (!isdir (dest))
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("installing multiple files, but last argument, %s \
|
|
||||||
is not a directory"),
|
|
||||||
quote (dest));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
for (i = 0; i < n_files - 1; i++)
|
|
||||||
{
|
|
||||||
errors |= install_file_in_dir (file[i], dest, &x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy file FROM onto file TO, creating any missing parent directories of TO.
|
|
||||||
Return 0 if successful, 1 if an error occurs */
|
|
||||||
|
|
||||||
static int
|
|
||||||
install_file_to_path (const char *from, const char *to,
|
|
||||||
const struct cp_options *x)
|
|
||||||
{
|
|
||||||
char *dest_dir;
|
|
||||||
int fail = 0;
|
|
||||||
|
|
||||||
dest_dir = dir_name (to);
|
|
||||||
|
|
||||||
/* check to make sure this is a path (not install a b ) */
|
|
||||||
if (!STREQ (dest_dir, ".")
|
|
||||||
&& !isdir (dest_dir))
|
|
||||||
{
|
|
||||||
/* Someone will probably ask for a new option or three to specify
|
|
||||||
owner, group, and permissions for parent directories. Remember
|
|
||||||
that this option is intended mainly to help installers when the
|
|
||||||
distribution doesn't provide proper install rules. */
|
|
||||||
#define DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
|
|
||||||
fail = make_path (dest_dir, DIR_MODE, DIR_MODE, owner_id, group_id, 0,
|
|
||||||
(x->verbose ? _("creating directory %s") : NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail == 0)
|
|
||||||
fail = install_file_in_file (from, to, x);
|
|
||||||
|
|
||||||
free (dest_dir);
|
|
||||||
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy file FROM onto file TO and give TO the appropriate
|
|
||||||
attributes.
|
|
||||||
Return 0 if successful, 1 if an error occurs. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
install_file_in_file (const char *from, const char *to,
|
|
||||||
const struct cp_options *x)
|
|
||||||
{
|
|
||||||
if (copy_file (from, to, x))
|
|
||||||
return 1;
|
|
||||||
if (strip_files)
|
|
||||||
strip (to);
|
|
||||||
if (change_attributes (to))
|
|
||||||
return 1;
|
|
||||||
if (x->preserve_timestamps)
|
|
||||||
return change_timestamps (from, to);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy file FROM into directory TO_DIR, keeping its same name,
|
|
||||||
and give the copy the appropriate attributes.
|
|
||||||
Return 0 if successful, 1 if not. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
install_file_in_dir (const char *from, const char *to_dir,
|
|
||||||
const struct cp_options *x)
|
|
||||||
{
|
|
||||||
const char *from_base;
|
|
||||||
char *to;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
from_base = base_name (from);
|
|
||||||
to = path_concat (to_dir, from_base, NULL);
|
|
||||||
ret = install_file_in_file (from, to, x);
|
|
||||||
free (to);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy file FROM onto file TO, creating TO if necessary.
|
|
||||||
Return 0 if the copy is successful, 1 if not. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
copy_file (const char *from, const char *to, const struct cp_options *x)
|
|
||||||
{
|
|
||||||
int fail;
|
|
||||||
int nonexistent_dst = 0;
|
|
||||||
int copy_into_self;
|
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
if (isdir (from))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s is a directory"), quote (from));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fail = copy (from, to, nonexistent_dst, x, ©_into_self, NULL);
|
|
||||||
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the attributes of file or directory PATH.
|
|
||||||
Return 0 if successful, 1 if not. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_attributes (const char *path)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
/* 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. But AFS returns EPERM when you try to change a
|
|
||||||
file's group; thus the kludge. */
|
|
||||||
|
|
||||||
if (chown (path, owner_id, group_id)
|
|
||||||
#ifdef AFS
|
|
||||||
&& errno != EPERM
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
|
||||||
error (0, errno, "cannot change ownership of %s", quote (path));
|
|
||||||
err = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err && chmod (path, mode))
|
|
||||||
{
|
|
||||||
error (0, errno, "cannot change permissions of %s", quote (path));
|
|
||||||
err = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the timestamps of file TO to match those of file FROM.
|
|
||||||
Return 0 if successful, 1 if not. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
change_timestamps (const char *from, const char *to)
|
|
||||||
{
|
|
||||||
struct stat stb;
|
|
||||||
struct utimbuf utb;
|
|
||||||
|
|
||||||
if (stat (from, &stb))
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot obtain time stamps for %s"), quote (from));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* There's currently no interface to set file timestamps with
|
|
||||||
better than 1-second resolution, so discard any fractional
|
|
||||||
part of the source timestamp. */
|
|
||||||
|
|
||||||
utb.actime = stb.st_atime;
|
|
||||||
utb.modtime = stb.st_mtime;
|
|
||||||
if (utime (to, &utb))
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot set time stamps for %s"), quote (to));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Strip the symbol table from the file PATH.
|
|
||||||
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 (const char *path)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
pid_t pid = fork ();
|
|
||||||
|
|
||||||
switch (pid)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
error (1, errno, _("cannot fork"));
|
|
||||||
break;
|
|
||||||
case 0: /* Child. */
|
|
||||||
execlp ("strip", "strip", path, NULL);
|
|
||||||
error (1, errno, _("cannot run strip"));
|
|
||||||
break;
|
|
||||||
default: /* Parent. */
|
|
||||||
/* Parent process. */
|
|
||||||
while (pid != wait (&status)) /* Wait for kid to finish. */
|
|
||||||
/* Do nothing. */ ;
|
|
||||||
if (status)
|
|
||||||
error (1, 0, _("strip failed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the user and group ownership of the files to install. */
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_ids (void)
|
|
||||||
{
|
|
||||||
struct passwd *pw;
|
|
||||||
struct group *gr;
|
|
||||||
|
|
||||||
if (owner_name)
|
|
||||||
{
|
|
||||||
pw = getpwnam (owner_name);
|
|
||||||
if (pw == NULL)
|
|
||||||
{
|
|
||||||
long int tmp_long;
|
|
||||||
if (xstrtol (owner_name, NULL, 0, &tmp_long, NULL) != LONGINT_OK
|
|
||||||
|| tmp_long < 0 || tmp_long > UID_T_MAX)
|
|
||||||
error (1, 0, _("invalid user %s"), quote (owner_name));
|
|
||||||
owner_id = (uid_t) tmp_long;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
owner_id = pw->pw_uid;
|
|
||||||
endpwent ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
owner_id = (uid_t) -1;
|
|
||||||
|
|
||||||
if (group_name)
|
|
||||||
{
|
|
||||||
gr = getgrnam (group_name);
|
|
||||||
if (gr == NULL)
|
|
||||||
{
|
|
||||||
long int tmp_long;
|
|
||||||
if (xstrtol (group_name, NULL, 0, &tmp_long, NULL) != LONGINT_OK
|
|
||||||
|| tmp_long < 0 || tmp_long > GID_T_MAX)
|
|
||||||
error (1, 0, _("invalid group %s"), quote (group_name));
|
|
||||||
group_id = (gid_t) tmp_long;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
group_id = gr->gr_gid;
|
|
||||||
endgrent ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
group_id = (gid_t) -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... SOURCE DEST (1st format)\n\
|
|
||||||
or: %s [OPTION]... SOURCE... DIRECTORY (2nd format)\n\
|
|
||||||
or: %s -d [OPTION]... DIRECTORY... (3rd format)\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
In the first two formats, copy SOURCE to DEST or multiple SOURCE(s) to\n\
|
|
||||||
the existing DIRECTORY, while setting permission modes and owner/group.\n\
|
|
||||||
In the third format, create all components of the given DIRECTORY(ies).\n\
|
|
||||||
\n\
|
|
||||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
|
||||||
-b like --backup but does not accept an argument\n\
|
|
||||||
-c (ignored)\n\
|
|
||||||
-d, --directory treat all arguments as directory names; create all\n\
|
|
||||||
components of the specified directories\n\
|
|
||||||
-D create all leading components of DEST except the last,\n\
|
|
||||||
then copy SOURCE to DEST; useful in the 1st format\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\
|
|
||||||
-p, --preserve-timestamps apply access/modification times of SOURCE files\n\
|
|
||||||
to corresponding destination files\n\
|
|
||||||
-s, --strip strip symbol tables, only for 1st and 2nd formats\n\
|
|
||||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
|
||||||
-v, --verbose print the name of each directory as it is created\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
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\
|
|
||||||
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\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
@@ -1,563 +0,0 @@
|
|||||||
/* `ln' program to create links between files.
|
|
||||||
Copyright (C) 86, 89, 90, 91, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by Mike Parker and David MacKenzie. */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "same.h"
|
|
||||||
#include "backupfile.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "ln"
|
|
||||||
|
|
||||||
#define AUTHORS "Mike Parker and David MacKenzie"
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TARGET_DIRECTORY_OPTION = CHAR_MAX + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
int link (); /* Some systems don't declare this anywhere. */
|
|
||||||
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
int symlink ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* In being careful not even to try to make hard links to directories,
|
|
||||||
we have to know whether link(2) follows symlinks. If it does, then
|
|
||||||
we have to *stat* the `source' to see if the resulting link would be
|
|
||||||
to a directory. Otherwise, we have to use *lstat* so that we allow
|
|
||||||
users to make hard links to symlinks-that-point-to-directories. */
|
|
||||||
|
|
||||||
#if LINK_FOLLOWS_SYMLINKS
|
|
||||||
# define STAT_LIKE_LINK(File, Stat_buf) \
|
|
||||||
stat (File, Stat_buf)
|
|
||||||
#else
|
|
||||||
# define STAT_LIKE_LINK(File, Stat_buf) \
|
|
||||||
lstat (File, Stat_buf)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Construct a string NEW_DEST by concatenating DEST, a slash, and
|
|
||||||
basename(SOURCE) in alloca'd memory. Don't modify DEST or SOURCE. */
|
|
||||||
|
|
||||||
#define PATH_BASENAME_CONCAT(new_dest, dest, source) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
const char *source_base; \
|
|
||||||
char *tmp_source; \
|
|
||||||
\
|
|
||||||
tmp_source = (char *) alloca (strlen ((source)) + 1); \
|
|
||||||
strcpy (tmp_source, (source)); \
|
|
||||||
strip_trailing_slashes (tmp_source); \
|
|
||||||
source_base = base_name (tmp_source); \
|
|
||||||
\
|
|
||||||
(new_dest) = (char *) alloca (strlen ((dest)) + 1 \
|
|
||||||
+ strlen (source_base) + 1); \
|
|
||||||
stpcpy (stpcpy (stpcpy ((new_dest), (dest)), "/"), source_base);\
|
|
||||||
} \
|
|
||||||
while (0)
|
|
||||||
|
|
||||||
int isdir ();
|
|
||||||
int yesno ();
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* The name by which the program was run, for error messages. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* FIXME: document */
|
|
||||||
enum backup_type backup_type;
|
|
||||||
|
|
||||||
/* A pointer to the function used to make links. This will point to either
|
|
||||||
`link' or `symlink'. */
|
|
||||||
static int (*linkfunc) ();
|
|
||||||
|
|
||||||
/* If nonzero, make symbolic links; otherwise, make hard links. */
|
|
||||||
static int symbolic_link;
|
|
||||||
|
|
||||||
/* If nonzero, ask the user before removing existing files. */
|
|
||||||
static int interactive;
|
|
||||||
|
|
||||||
/* If nonzero, remove existing files unconditionally. */
|
|
||||||
static int remove_existing_files;
|
|
||||||
|
|
||||||
/* If nonzero, list each file as it is moved. */
|
|
||||||
static int verbose;
|
|
||||||
|
|
||||||
/* If nonzero, allow the superuser to make hard links to directories. */
|
|
||||||
static int hard_dir_link;
|
|
||||||
|
|
||||||
/* If nonzero, and the specified destination is a symbolic link to a
|
|
||||||
directory, treat it just as if it were a directory. Otherwise, the
|
|
||||||
command `ln --force --no-dereference file symlink-to-dir' deletes
|
|
||||||
symlink-to-dir before creating the new link. */
|
|
||||||
static int dereference_dest_dir_symlinks = 1;
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"backup", optional_argument, NULL, 'b'},
|
|
||||||
{"directory", no_argument, NULL, 'F'},
|
|
||||||
{"no-dereference", no_argument, NULL, 'n'},
|
|
||||||
{"force", no_argument, NULL, 'f'},
|
|
||||||
{"interactive", no_argument, NULL, 'i'},
|
|
||||||
{"suffix", required_argument, NULL, 'S'},
|
|
||||||
{"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
|
|
||||||
{"symbolic", no_argument, NULL, 's'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Make a link DEST to the (usually) existing file SOURCE.
|
|
||||||
Symbolic links to nonexistent files are allowed.
|
|
||||||
If DEST is a directory, put the link to SOURCE in that directory.
|
|
||||||
Return 1 if there is an error, otherwise 0. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
do_link (const char *source, const char *dest)
|
|
||||||
{
|
|
||||||
struct stat source_stats;
|
|
||||||
struct stat dest_stats;
|
|
||||||
char *dest_backup = NULL;
|
|
||||||
int lstat_status;
|
|
||||||
int backup_succeeded = 0;
|
|
||||||
|
|
||||||
/* Use stat here instead of lstat.
|
|
||||||
On SVR4, link does not follow symlinks, so this check disallows
|
|
||||||
making hard links to symlinks that point to directories. Big deal.
|
|
||||||
On other systems, link follows symlinks, so this check is right. */
|
|
||||||
if (!symbolic_link)
|
|
||||||
{
|
|
||||||
if (STAT_LIKE_LINK (source, &source_stats) != 0)
|
|
||||||
{
|
|
||||||
error (0, errno, _("accessing %s"), quote (source));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (S_ISLNK (source_stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s: warning: making a hard link to a symbolic link\
|
|
||||||
is not portable"),
|
|
||||||
quote (source));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hard_dir_link && S_ISDIR (source_stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s: hard link not allowed for directory"),
|
|
||||||
quote (source));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lstat_status = lstat (dest, &dest_stats);
|
|
||||||
if (lstat_status != 0 && errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("accessing %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the destination is a directory or (it is a symlink to a directory
|
|
||||||
and the user has not specified --no-dereference), then form the
|
|
||||||
actual destination name by appending base_name (source) to the
|
|
||||||
specified destination directory. */
|
|
||||||
if ((lstat_status == 0
|
|
||||||
&& S_ISDIR (dest_stats.st_mode))
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
|| (dereference_dest_dir_symlinks
|
|
||||||
&& (lstat_status == 0
|
|
||||||
&& S_ISLNK (dest_stats.st_mode)
|
|
||||||
&& isdir (dest)))
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
|
||||||
/* Target is a directory; build the full filename. */
|
|
||||||
char *new_dest;
|
|
||||||
PATH_BASENAME_CONCAT (new_dest, dest, source);
|
|
||||||
dest = new_dest;
|
|
||||||
|
|
||||||
/* Get stats for new DEST. */
|
|
||||||
lstat_status = lstat (dest, &dest_stats);
|
|
||||||
if (lstat_status != 0 && errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("accessing %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If --force (-f) has been specified without --backup, then before
|
|
||||||
making a link ln must remove the destination file if it exists.
|
|
||||||
(with --backup, it just renames any existing destination file)
|
|
||||||
But if the source and destination are the same, don't remove
|
|
||||||
anything and fail right here. */
|
|
||||||
if (remove_existing_files
|
|
||||||
&& lstat_status == 0
|
|
||||||
/* Allow `ln -sf --backup k k' to succeed in creating the
|
|
||||||
self-referential symlink, but don't allow the hard-linking
|
|
||||||
equivalent: `ln -f k k' (with or without --backup) to get
|
|
||||||
beyond this point, because the error message you'd get is
|
|
||||||
misleading. */
|
|
||||||
&& (backup_type == none || !symlink)
|
|
||||||
&& (!symbolic_link || stat (source, &source_stats) == 0)
|
|
||||||
&& source_stats.st_dev == dest_stats.st_dev
|
|
||||||
&& source_stats.st_ino == dest_stats.st_ino
|
|
||||||
/* The following detects whether removing DEST will also remove
|
|
||||||
SOURCE. If the file has only one link then both are surely
|
|
||||||
the same link. Otherwise check whether they point to the same
|
|
||||||
name in the same directory. */
|
|
||||||
&& (source_stats.st_nlink == 1 || same_name (source, dest)))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s and %s are the same file"),
|
|
||||||
quote_n (0, source), quote_n (1, dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lstat_status == 0 || lstat (dest, &dest_stats) == 0)
|
|
||||||
{
|
|
||||||
if (S_ISDIR (dest_stats.st_mode))
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s: cannot overwrite directory"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (interactive)
|
|
||||||
{
|
|
||||||
fprintf (stderr, _("%s: replace %s? "), program_name, quote (dest));
|
|
||||||
if (!yesno ())
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (!remove_existing_files && backup_type == none)
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s: File exists"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backup_type != none)
|
|
||||||
{
|
|
||||||
char *tmp_backup = find_backup_file_name (dest, backup_type);
|
|
||||||
if (tmp_backup == NULL)
|
|
||||||
xalloc_die ();
|
|
||||||
dest_backup = (char *) alloca (strlen (tmp_backup) + 1);
|
|
||||||
strcpy (dest_backup, tmp_backup);
|
|
||||||
free (tmp_backup);
|
|
||||||
if (rename (dest, dest_backup))
|
|
||||||
{
|
|
||||||
if (errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot backup %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dest_backup = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
backup_succeeded = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to unlink DEST even if we may have renamed it. In some unusual
|
|
||||||
cases (when DEST and DEST_BACKUP are hard-links that refer to the
|
|
||||||
same file), rename succeeds and DEST remains. If we didn't remove
|
|
||||||
DEST in that case, the subsequent LINKFUNC call would fail. */
|
|
||||||
if (unlink (dest) && errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot remove %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (errno != ENOENT)
|
|
||||||
{
|
|
||||||
error (0, errno, _("accessing %s"), quote (dest));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
{
|
|
||||||
printf ((symbolic_link
|
|
||||||
? _("create symbolic link %s to %s")
|
|
||||||
: _("create hard link %s to %s")),
|
|
||||||
quote_n (0, dest), quote_n (1, source));
|
|
||||||
if (backup_succeeded)
|
|
||||||
printf (_(" (backup: %s)"), quote (dest_backup));
|
|
||||||
putchar ('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*linkfunc) (source, dest) == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
error (0, errno,
|
|
||||||
(symbolic_link
|
|
||||||
? _("creating symbolic link %s to %s")
|
|
||||||
: _("creating hard link %s to %s")),
|
|
||||||
quote_n (0, dest), quote_n (1, source));
|
|
||||||
|
|
||||||
if (dest_backup)
|
|
||||||
{
|
|
||||||
if (rename (dest_backup, dest))
|
|
||||||
error (0, errno, _("cannot un-backup %s"), quote (dest));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... TARGET [LINK_NAME]\n\
|
|
||||||
or: %s [OPTION]... TARGET... DIRECTORY\n\
|
|
||||||
or: %s [OPTION]... --target-directory=DIRECTORY TARGET...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Create a link to the specified TARGET with optional LINK_NAME.\n\
|
|
||||||
If LINK_NAME is omitted, a link with the same basename as the TARGET is\n\
|
|
||||||
created in the current directory. When using the second form with more\n\
|
|
||||||
than one TARGET, the last argument must be a directory; create links\n\
|
|
||||||
in DIRECTORY to each TARGET. Create hard links by default, symbolic\n\
|
|
||||||
links with --symbolic. When creating hard links, each TARGET must exist.\n\
|
|
||||||
\n\
|
|
||||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
|
||||||
-b like --backup but does not accept an argument\n\
|
|
||||||
-d, -F, --directory hard link directories (super-user only)\n\
|
|
||||||
-f, --force remove existing destination files\n\
|
|
||||||
-n, --no-dereference treat destination that is a symlink to a\n\
|
|
||||||
directory as if it were a normal file\n\
|
|
||||||
-i, --interactive prompt whether to remove destinations\n\
|
|
||||||
-s, --symbolic make symbolic links instead of hard links\n\
|
|
||||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
|
||||||
--target-directory=DIRECTORY specify the DIRECTORY in which to create\n\
|
|
||||||
the links\n\
|
|
||||||
-v, --verbose print name of each file before linking\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
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\
|
|
||||||
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\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
int errors;
|
|
||||||
int make_backups = 0;
|
|
||||||
char *backup_suffix_string;
|
|
||||||
char *version_control_string = NULL;
|
|
||||||
char *target_directory = NULL;
|
|
||||||
int target_directory_specified;
|
|
||||||
unsigned int n_files;
|
|
||||||
char **file;
|
|
||||||
int dest_is_dir;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
|
|
||||||
we'll actually use backup_suffix_string. */
|
|
||||||
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
|
|
||||||
|
|
||||||
symbolic_link = remove_existing_files = interactive = verbose
|
|
||||||
= hard_dir_link = 0;
|
|
||||||
errors = 0;
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "bdfinsvFS:V:", long_options, NULL))
|
|
||||||
!= -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0: /* Long-named option. */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'V': /* FIXME: this is deprecated. Remove it in 2001. */
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: --version-control (-V) is obsolete; support for\
|
|
||||||
it\nwill be removed in some future release. Use --backup=%s instead."
|
|
||||||
), optarg);
|
|
||||||
/* Fall through. */
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
make_backups = 1;
|
|
||||||
if (optarg)
|
|
||||||
version_control_string = optarg;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
case 'F':
|
|
||||||
hard_dir_link = 1;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
remove_existing_files = 1;
|
|
||||||
interactive = 0;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
remove_existing_files = 0;
|
|
||||||
interactive = 1;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
dereference_dest_dir_symlinks = 0;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
#ifdef S_ISLNK
|
|
||||||
symbolic_link = 1;
|
|
||||||
#else
|
|
||||||
error (1, 0, _("symbolic links are not supported on this system"));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case TARGET_DIRECTORY_OPTION:
|
|
||||||
target_directory = optarg;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose = 1;
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
make_backups = 1;
|
|
||||||
backup_suffix_string = optarg;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n_files = argc - optind;
|
|
||||||
file = argv + optind;
|
|
||||||
|
|
||||||
if (n_files == 0)
|
|
||||||
{
|
|
||||||
error (0, 0, _("missing file argument"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
target_directory_specified = (target_directory != NULL);
|
|
||||||
if (!target_directory)
|
|
||||||
target_directory = file[n_files - 1];
|
|
||||||
|
|
||||||
/* If there's only one file argument, then pretend `.' was given
|
|
||||||
as the second argument. */
|
|
||||||
if (n_files == 1)
|
|
||||||
{
|
|
||||||
static char *dummy[2];
|
|
||||||
dummy[0] = file[0];
|
|
||||||
dummy[1] = ".";
|
|
||||||
file = dummy;
|
|
||||||
n_files = 2;
|
|
||||||
dest_is_dir = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dest_is_dir = isdir (target_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbolic_link)
|
|
||||||
linkfunc = symlink;
|
|
||||||
else
|
|
||||||
linkfunc = link;
|
|
||||||
|
|
||||||
if (target_directory_specified && !dest_is_dir)
|
|
||||||
{
|
|
||||||
error (0, 0, _("specified target directory, %s is not a directory"),
|
|
||||||
quote (target_directory));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backup_suffix_string)
|
|
||||||
simple_backup_suffix = xstrdup (backup_suffix_string);
|
|
||||||
|
|
||||||
backup_type = (make_backups
|
|
||||||
? xget_version (_("backup type"), version_control_string)
|
|
||||||
: none);
|
|
||||||
|
|
||||||
if (target_directory_specified || n_files > 2)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
unsigned int last_file_idx = (target_directory_specified
|
|
||||||
? n_files - 1
|
|
||||||
: n_files - 2);
|
|
||||||
|
|
||||||
if (!target_directory_specified && !dest_is_dir)
|
|
||||||
error (1, 0,
|
|
||||||
_("when making multiple links, last argument must be a directory"));
|
|
||||||
for (i = 0; i <= last_file_idx; ++i)
|
|
||||||
errors += do_link (file[i], target_directory);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct stat source_stats;
|
|
||||||
const char *source;
|
|
||||||
char *dest;
|
|
||||||
char *new_dest;
|
|
||||||
|
|
||||||
source = file[0];
|
|
||||||
dest = file[1];
|
|
||||||
|
|
||||||
/* When the destination is specified with a trailing slash and the
|
|
||||||
source exists but is not a directory, convert the user's command
|
|
||||||
`ln source dest/' to `ln source dest/basename(source)'. */
|
|
||||||
|
|
||||||
if (dest[strlen (dest) - 1] == '/'
|
|
||||||
&& lstat (source, &source_stats) == 0
|
|
||||||
&& !S_ISDIR (source_stats.st_mode))
|
|
||||||
{
|
|
||||||
PATH_BASENAME_CONCAT (new_dest, dest, source);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_dest = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
errors = do_link (source, new_dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors != 0);
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include "ls.h"
|
|
||||||
int ls_mode = LS_MULTI_COL;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include "ls.h"
|
|
||||||
int ls_mode = LS_LS;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include "ls.h"
|
|
||||||
int ls_mode = LS_LONG_FORMAT;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
|||||||
/* This is for the `ls' program. */
|
|
||||||
#define LS_LS 1
|
|
||||||
|
|
||||||
/* This is for the `dir' program. */
|
|
||||||
#define LS_MULTI_COL 2
|
|
||||||
|
|
||||||
/* This is for the `vdir' program. */
|
|
||||||
#define LS_LONG_FORMAT 3
|
|
||||||
|
|
||||||
extern int ls_mode;
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
/* mkdir -- make directories
|
|
||||||
Copyright (C) 90, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "dirname.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "makepath.h"
|
|
||||||
#include "modechange.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "mkdir"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, ensure that all parents of the specified directory exist. */
|
|
||||||
static int create_parents;
|
|
||||||
|
|
||||||
static struct option const longopts[] =
|
|
||||||
{
|
|
||||||
{"mode", required_argument, NULL, 'm'},
|
|
||||||
{"parents", no_argument, NULL, 'p'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION] DIRECTORY...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Create the DIRECTORY(ies), if they do not already exist.\n\
|
|
||||||
\n\
|
|
||||||
-m, --mode=MODE set permission mode (as in chmod), not rwxrwxrwx - umask\n\
|
|
||||||
-p, --parents no error if existing, make parent directories as needed\n\
|
|
||||||
-v, --verbose print a message for each created directory\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
mode_t newmode;
|
|
||||||
mode_t parent_mode;
|
|
||||||
const char *specified_mode = NULL;
|
|
||||||
const char *verbose_fmt_string = NULL;
|
|
||||||
int errors = 0;
|
|
||||||
int optc;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
create_parents = 0;
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0: /* Long option. */
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
create_parents = 1;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
specified_mode = optarg;
|
|
||||||
break;
|
|
||||||
case 'v': /* --verbose */
|
|
||||||
verbose_fmt_string = _("created directory %s");
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
newmode = S_IRWXUGO;
|
|
||||||
{
|
|
||||||
mode_t umask_value = umask (0);
|
|
||||||
umask (umask_value); /* Restore the old value. */
|
|
||||||
parent_mode = (newmode & (~ umask_value)) | S_IWUSR | S_IXUSR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (specified_mode)
|
|
||||||
{
|
|
||||||
struct mode_change *change = mode_compile (specified_mode, 0);
|
|
||||||
newmode &= ~ umask (0);
|
|
||||||
if (change == MODE_INVALID)
|
|
||||||
error (1, 0, _("invalid mode %s"), quote (specified_mode));
|
|
||||||
else if (change == MODE_MEMORY_EXHAUSTED)
|
|
||||||
xalloc_die ();
|
|
||||||
newmode = mode_adjust (newmode, change);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
{
|
|
||||||
int fail = 0;
|
|
||||||
|
|
||||||
/* Remove any trailing slashes. Not removing them would lead to calling
|
|
||||||
`mkdir ("dir/", mode)' for e.g., the commands `mkdir dir/' and
|
|
||||||
`mkdir -p dir/', and such a call fails on NetBSD systems when `dir'
|
|
||||||
doesn't already exist. */
|
|
||||||
strip_trailing_slashes (argv[optind]);
|
|
||||||
|
|
||||||
if (create_parents)
|
|
||||||
{
|
|
||||||
char *parents = dir_name (argv[optind]);
|
|
||||||
fail = make_path (parents, parent_mode, parent_mode,
|
|
||||||
-1, -1, 1, verbose_fmt_string);
|
|
||||||
free (parents);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail == 0)
|
|
||||||
{
|
|
||||||
const char *dir = argv[optind];
|
|
||||||
int dir_created;
|
|
||||||
fail = make_dir (dir, dir, newmode, &dir_created);
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
/* make_dir already gave a diagnostic. */
|
|
||||||
}
|
|
||||||
else if (!create_parents && !dir_created)
|
|
||||||
{
|
|
||||||
/* make_dir `succeeds' when DIR already exists.
|
|
||||||
In that case, mkdir must fail, unless --parents (-p)
|
|
||||||
was specified. */
|
|
||||||
error (0, EEXIST, _("cannot create directory %s"),
|
|
||||||
quote (dir));
|
|
||||||
fail = 1;
|
|
||||||
}
|
|
||||||
else if (verbose_fmt_string)
|
|
||||||
error (0, 0, verbose_fmt_string, quote (dir));
|
|
||||||
|
|
||||||
/* mkdir(2) is required to honor only the file permission bits.
|
|
||||||
In particular, it needn't do anything about `special' bits,
|
|
||||||
so if any were set in newmode, apply them with chmod.
|
|
||||||
This extra step is necessary in some cases when the containing
|
|
||||||
directory has a default ACL. */
|
|
||||||
|
|
||||||
/* Set the permissions only if this directory has just
|
|
||||||
been created. */
|
|
||||||
|
|
||||||
if (fail == 0 && specified_mode && dir_created)
|
|
||||||
{
|
|
||||||
fail = chmod (dir, newmode);
|
|
||||||
if (fail)
|
|
||||||
error (0, errno, _("cannot set permissions of directory %s"),
|
|
||||||
quote (dir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
errors = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
/* mkfifo -- make fifo's (named pipes)
|
|
||||||
Copyright (C) 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "modechange.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "mkfifo"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
static struct option const longopts[] =
|
|
||||||
{
|
|
||||||
{"mode", required_argument, NULL, 'm'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef S_ISFIFO
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION] NAME...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Create named pipes (FIFOs) with the given NAMEs.\n\
|
|
||||||
\n\
|
|
||||||
-m, --mode=MODE set permission mode (as in chmod), not a=rw - umask\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
mode_t newmode;
|
|
||||||
struct mode_change *change;
|
|
||||||
const char *specified_mode;
|
|
||||||
int errors = 0;
|
|
||||||
int optc;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
specified_mode = NULL;
|
|
||||||
|
|
||||||
#ifndef S_ISFIFO
|
|
||||||
error (4, 0, _("fifo files not supported"));
|
|
||||||
#else
|
|
||||||
while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
specified_mode = optarg;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
||||||
if (specified_mode)
|
|
||||||
{
|
|
||||||
newmode &= ~ umask (0);
|
|
||||||
change = mode_compile (specified_mode, 0);
|
|
||||||
if (change == MODE_INVALID)
|
|
||||||
error (1, 0, _("invalid mode"));
|
|
||||||
else if (change == MODE_MEMORY_EXHAUSTED)
|
|
||||||
xalloc_die ();
|
|
||||||
newmode = mode_adjust (newmode, change);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
{
|
|
||||||
int fail = mkfifo (argv[optind], newmode);
|
|
||||||
if (fail)
|
|
||||||
error (0, errno, _("cannot create fifo `%s'"), quote (argv[optind]));
|
|
||||||
|
|
||||||
/* If the containing directory happens to have a default ACL, chmod
|
|
||||||
ensures the file mode permission bits are still set as desired. */
|
|
||||||
|
|
||||||
if (fail == 0 && specified_mode)
|
|
||||||
{
|
|
||||||
fail = chmod (argv[optind], newmode);
|
|
||||||
if (fail)
|
|
||||||
error (0, errno, _("cannot set permissions of fifo `%s'"),
|
|
||||||
quote (argv[optind]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
errors = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
/* mknod -- make special files
|
|
||||||
Copyright (C) 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Usage: mknod [-m mode] [--mode=mode] path {bcu} major minor
|
|
||||||
make a block or character device node
|
|
||||||
mknod [-m mode] [--mode=mode] path p
|
|
||||||
make a FIFO (named pipe)
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-m, --mode=mode Set the mode of created nodes to MODE, which is
|
|
||||||
symbolic as in chmod and uses the umask as a point of
|
|
||||||
departure.
|
|
||||||
|
|
||||||
David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "modechange.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "xstrtol.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "mknod"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
static struct option const longopts[] =
|
|
||||||
{
|
|
||||||
{"mode", required_argument, NULL, 'm'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Create the special file NAME of the given TYPE.\n\
|
|
||||||
\n\
|
|
||||||
-m, --mode=MODE set permission mode (as in chmod), not a=rw - umask\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
MAJOR MINOR are forbidden for TYPE p, mandatory otherwise. TYPE may be:\n\
|
|
||||||
\n\
|
|
||||||
b create a block (buffered) special file\n\
|
|
||||||
c, u create a character (unbuffered) special file\n\
|
|
||||||
p create a FIFO\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
mode_t newmode;
|
|
||||||
struct mode_change *change;
|
|
||||||
const char *specified_mode;
|
|
||||||
int optc;
|
|
||||||
int i_major, i_minor;
|
|
||||||
long int tmp_major, tmp_minor;
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
specified_mode = NULL;
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
specified_mode = optarg;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
||||||
if (specified_mode)
|
|
||||||
{
|
|
||||||
newmode &= ~ umask (0);
|
|
||||||
change = mode_compile (specified_mode, 0);
|
|
||||||
if (change == MODE_INVALID)
|
|
||||||
error (1, 0, _("invalid mode"));
|
|
||||||
else if (change == MODE_MEMORY_EXHAUSTED)
|
|
||||||
xalloc_die ();
|
|
||||||
newmode = mode_adjust (newmode, change);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc - optind != 2 && argc - optind != 4)
|
|
||||||
{
|
|
||||||
const char *msg;
|
|
||||||
if (argc - optind < 2)
|
|
||||||
msg = _("too few arguments");
|
|
||||||
else if (argc - optind > 4)
|
|
||||||
msg = _("too many arguments");
|
|
||||||
else
|
|
||||||
msg = _("wrong number of arguments");
|
|
||||||
error (0, 0, msg);
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only check the first character, to allow mnemonic usage like
|
|
||||||
`mknod /dev/rst0 character 18 0'. */
|
|
||||||
|
|
||||||
switch (argv[optind + 1][0])
|
|
||||||
{
|
|
||||||
case 'b': /* `block' or `buffered' */
|
|
||||||
#ifndef S_IFBLK
|
|
||||||
error (4, 0, _("block special files not supported"));
|
|
||||||
#else
|
|
||||||
if (argc - optind != 4)
|
|
||||||
{
|
|
||||||
error (0, 0, _("\
|
|
||||||
when creating block special files, major and minor device\n\
|
|
||||||
numbers must be specified"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
s = argv[optind + 2];
|
|
||||||
if (xstrtol (s, NULL, 0, &tmp_major, NULL) != LONGINT_OK)
|
|
||||||
error (1, 0, _("invalid major device number %s"), quote (s));
|
|
||||||
|
|
||||||
s = argv[optind + 3];
|
|
||||||
if (xstrtol (s, NULL, 0, &tmp_minor, NULL) != LONGINT_OK)
|
|
||||||
error (1, 0, _("invalid minor device number %s"), quote (s));
|
|
||||||
|
|
||||||
i_major = (int) tmp_major;
|
|
||||||
i_minor = (int) tmp_minor;
|
|
||||||
|
|
||||||
if (mknod (argv[optind], newmode | S_IFBLK, makedev (i_major, i_minor)))
|
|
||||||
error (1, errno, "%s", quote (argv[optind]));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'c': /* `character' */
|
|
||||||
case 'u': /* `unbuffered' */
|
|
||||||
#ifndef S_IFCHR
|
|
||||||
error (4, 0, _("character special files not supported"));
|
|
||||||
#else
|
|
||||||
if (argc - optind != 4)
|
|
||||||
{
|
|
||||||
error (0, 0, _("\
|
|
||||||
when creating character special files, major and minor device\n\
|
|
||||||
numbers must be specified"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
s = argv[optind + 2];
|
|
||||||
if (xstrtol (s, NULL, 0, &tmp_major, NULL) != LONGINT_OK)
|
|
||||||
error (1, 0, _("invalid major device number %s"), quote (s));
|
|
||||||
|
|
||||||
s = argv[optind + 3];
|
|
||||||
if (xstrtol (s, NULL, 0, &tmp_minor, NULL) != LONGINT_OK)
|
|
||||||
error (1, 0, _("invalid minor device number %s"), quote (s));
|
|
||||||
|
|
||||||
i_major = (int) tmp_major;
|
|
||||||
i_minor = (int) tmp_minor;
|
|
||||||
|
|
||||||
if (mknod (argv[optind], newmode | S_IFCHR, makedev (i_major, i_minor)))
|
|
||||||
error (1, errno, "%s", quote (argv[optind]));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p': /* `pipe' */
|
|
||||||
#ifndef S_ISFIFO
|
|
||||||
error (4, 0, _("fifo files not supported"));
|
|
||||||
#else
|
|
||||||
if (argc - optind != 2)
|
|
||||||
{
|
|
||||||
error (0, 0, _("\
|
|
||||||
major and minor device numbers may not be specified for fifo files"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
if (mkfifo (argv[optind], newmode))
|
|
||||||
error (1, errno, "%s", quote (argv[optind]));
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform an explicit chmod to ensure the file mode permission bits
|
|
||||||
are set as specified. This extra step is necessary in some cases
|
|
||||||
when the containing directory has a default ACL. */
|
|
||||||
|
|
||||||
if (specified_mode)
|
|
||||||
{
|
|
||||||
if (chmod (argv[optind], newmode))
|
|
||||||
error (0, errno, _("cannot set permissions of `%s'"),
|
|
||||||
quote (argv[optind]));
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
/* mv -- move or rename files
|
|
||||||
Copyright (C) 86, 89, 90, 91, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by Mike Parker, David MacKenzie, and Jim Meyering */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "backupfile.h"
|
|
||||||
#include "copy.h"
|
|
||||||
#include "cp-hash.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "path-concat.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "remove.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "mv"
|
|
||||||
|
|
||||||
#define AUTHORS "Mike Parker, David MacKenzie, and Jim Meyering"
|
|
||||||
|
|
||||||
/* Initial number of entries in each hash table entry's table of inodes. */
|
|
||||||
#define INITIAL_HASH_MODULE 100
|
|
||||||
|
|
||||||
/* Initial number of entries in the inode hash table. */
|
|
||||||
#define INITIAL_ENTRY_TAB_SIZE 70
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
|
|
||||||
STRIP_TRAILING_SLASHES_OPTION
|
|
||||||
};
|
|
||||||
|
|
||||||
int euidaccess ();
|
|
||||||
int full_write ();
|
|
||||||
int isdir ();
|
|
||||||
int lstat ();
|
|
||||||
int yesno ();
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* Remove any trailing slashes from each SOURCE argument. */
|
|
||||||
static int remove_trailing_slashes;
|
|
||||||
|
|
||||||
static struct option const long_options[] =
|
|
||||||
{
|
|
||||||
{"backup", optional_argument, NULL, 'b'},
|
|
||||||
{"force", no_argument, NULL, 'f'},
|
|
||||||
{"interactive", no_argument, NULL, 'i'},
|
|
||||||
{"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
|
|
||||||
{"suffix", required_argument, NULL, 'S'},
|
|
||||||
{"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
|
|
||||||
{"update", no_argument, NULL, 'u'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{"version-control", required_argument, NULL, 'V'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
rm_option_init (struct rm_options *x)
|
|
||||||
{
|
|
||||||
x->unlink_dirs = 0;
|
|
||||||
|
|
||||||
x->ignore_missing_files = 0;
|
|
||||||
|
|
||||||
x->recursive = 1;
|
|
||||||
|
|
||||||
/* Should we prompt for removal, too? No. Prompting for the `move'
|
|
||||||
part is enough. It implies removal. */
|
|
||||||
x->interactive = 0;
|
|
||||||
x->stdin_tty = 0;
|
|
||||||
|
|
||||||
x->verbose = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
cp_option_init (struct cp_options *x)
|
|
||||||
{
|
|
||||||
x->copy_as_regular = 0; /* FIXME: maybe make this an option */
|
|
||||||
x->dereference = DEREF_NEVER;
|
|
||||||
x->unlink_dest_before_opening = 0;
|
|
||||||
x->unlink_dest_after_failed_open = 0;
|
|
||||||
x->failed_unlink_is_fatal = 1;
|
|
||||||
x->hard_link = 0;
|
|
||||||
x->interactive = 0;
|
|
||||||
x->move_mode = 1;
|
|
||||||
x->myeuid = geteuid ();
|
|
||||||
x->one_file_system = 0;
|
|
||||||
x->preserve_owner_and_group = 1;
|
|
||||||
x->preserve_chmod_bits = 1;
|
|
||||||
x->preserve_timestamps = 1;
|
|
||||||
x->require_preserve = 0; /* FIXME: maybe make this an option */
|
|
||||||
x->recursive = 1;
|
|
||||||
x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */
|
|
||||||
x->symbolic_link = 0;
|
|
||||||
x->set_mode = 0;
|
|
||||||
x->mode = 0;
|
|
||||||
|
|
||||||
/* Find out the current file creation mask, to knock the right bits
|
|
||||||
when using chmod. The creation mask is set to be liberal, so
|
|
||||||
that created directories can be written, even if it would not
|
|
||||||
have been allowed with the mask this process was started with. */
|
|
||||||
x->umask_kill = ~ umask (0);
|
|
||||||
|
|
||||||
x->update = 0;
|
|
||||||
x->verbose = 0;
|
|
||||||
x->xstat = lstat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If PATH is an existing directory, return nonzero, else 0. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
is_real_dir (const char *path)
|
|
||||||
{
|
|
||||||
struct stat stats;
|
|
||||||
|
|
||||||
return lstat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
strip_trailing_slashes_2 (char *path)
|
|
||||||
{
|
|
||||||
char *end_p = path + strlen (path) - 1;
|
|
||||||
char *slash = end_p;
|
|
||||||
|
|
||||||
while (slash > path && *slash == '/')
|
|
||||||
*slash-- = '\0';
|
|
||||||
|
|
||||||
return slash < end_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move SOURCE onto DEST. Handles cross-filesystem moves.
|
|
||||||
If SOURCE is a directory, DEST must not exist.
|
|
||||||
Return 0 if successful, non-zero if an error occurred. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
do_move (const char *source, const char *dest, const struct cp_options *x)
|
|
||||||
{
|
|
||||||
static int first = 1;
|
|
||||||
int copy_into_self;
|
|
||||||
int rename_succeeded;
|
|
||||||
int fail;
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
{
|
|
||||||
first = 0;
|
|
||||||
|
|
||||||
/* Allocate space for remembering copied and created files. */
|
|
||||||
hash_init (INITIAL_HASH_MODULE, INITIAL_ENTRY_TAB_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fail = copy (source, dest, 0, x, ©_into_self, &rename_succeeded);
|
|
||||||
|
|
||||||
if (!fail)
|
|
||||||
{
|
|
||||||
const char *dir_to_remove;
|
|
||||||
if (copy_into_self)
|
|
||||||
{
|
|
||||||
/* In general, when copy returns with copy_into_self set, SOURCE is
|
|
||||||
the same as, or a parent of DEST. In this case we know it's a
|
|
||||||
parent. It doesn't make sense to move a directory into itself, and
|
|
||||||
besides in some situations doing so would give highly nonintuitive
|
|
||||||
results. Run this `mkdir b; touch a c; mv * b' in an empty
|
|
||||||
directory. Here's the result of running echo `find b -print`:
|
|
||||||
b b/a b/b b/b/a b/c. Notice that only file `a' was copied
|
|
||||||
into b/b. Handle this by giving a diagnostic, removing the
|
|
||||||
copied-into-self directory, DEST (`b/b' in the example),
|
|
||||||
and failing. */
|
|
||||||
|
|
||||||
dir_to_remove = NULL;
|
|
||||||
fail = 1;
|
|
||||||
}
|
|
||||||
else if (rename_succeeded)
|
|
||||||
{
|
|
||||||
/* No need to remove anything. SOURCE was successfully
|
|
||||||
renamed to DEST. */
|
|
||||||
dir_to_remove = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* This may mean SOURCE and DEST referred to different devices.
|
|
||||||
It may also conceivably mean that even though they referred
|
|
||||||
to the same device, rename wasn't implemented for that device.
|
|
||||||
|
|
||||||
E.g., (from Joel N. Weber),
|
|
||||||
[...] there might someday be cases where you can't rename
|
|
||||||
but you can copy where the device name is the same, especially
|
|
||||||
on Hurd. Consider an ftpfs with a primitive ftp server that
|
|
||||||
supports uploading, downloading and deleting, but not renaming.
|
|
||||||
|
|
||||||
Also, note that comparing device numbers is not a reliable
|
|
||||||
check for `can-rename'. Some systems can be set up so that
|
|
||||||
files from many different physical devices all have the same
|
|
||||||
st_dev field. This is a feature of some NFS mounting
|
|
||||||
configurations.
|
|
||||||
|
|
||||||
We reach this point if SOURCE has been successfully copied
|
|
||||||
to DEST. Now we have to remove SOURCE.
|
|
||||||
|
|
||||||
This function used to resort to copying only when rename
|
|
||||||
failed and set errno to EXDEV. */
|
|
||||||
|
|
||||||
dir_to_remove = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir_to_remove != NULL)
|
|
||||||
{
|
|
||||||
struct rm_options rm_options;
|
|
||||||
struct File_spec fs;
|
|
||||||
enum RM_status status;
|
|
||||||
|
|
||||||
rm_option_init (&rm_options);
|
|
||||||
rm_options.verbose = x->verbose;
|
|
||||||
|
|
||||||
remove_init ();
|
|
||||||
|
|
||||||
fspec_init_file (&fs, dir_to_remove);
|
|
||||||
|
|
||||||
/* Remove any trailing slashes. This is necessary if we
|
|
||||||
took the else branch of movefile. */
|
|
||||||
strip_trailing_slashes_2 (fs.filename);
|
|
||||||
|
|
||||||
status = rm (&fs, 1, &rm_options);
|
|
||||||
assert (VALID_STATUS (status));
|
|
||||||
if (status == RM_ERROR)
|
|
||||||
fail = 1;
|
|
||||||
|
|
||||||
remove_fini ();
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
error (0, errno, _("cannot remove %s"), quote (dir_to_remove));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move file SOURCE onto DEST. Handles the case when DEST is a directory.
|
|
||||||
DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a
|
|
||||||
directory and zero otherwise.
|
|
||||||
Return 0 if successful, non-zero if an error occurred. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
movefile (char *source, char *dest, int dest_is_dir,
|
|
||||||
const struct cp_options *x)
|
|
||||||
{
|
|
||||||
int dest_had_trailing_slash = strip_trailing_slashes_2 (dest);
|
|
||||||
int fail;
|
|
||||||
|
|
||||||
/* This code was introduced to handle the ambiguity in the semantics
|
|
||||||
of mv that is induced by the varying semantics of the rename function.
|
|
||||||
Some systems (e.g., Linux) have a rename function that honors a
|
|
||||||
trailing slash, while others (like Solaris 5,6,7) have a rename
|
|
||||||
function that ignores a trailing slash. I believe the Linux
|
|
||||||
rename semantics are POSIX and susv2 compliant. */
|
|
||||||
|
|
||||||
if (remove_trailing_slashes)
|
|
||||||
strip_trailing_slashes_2 (source);
|
|
||||||
|
|
||||||
/* In addition to when DEST is a directory, if DEST has a trailing
|
|
||||||
slash and neither SOURCE nor DEST is a directory, presume the target
|
|
||||||
is DEST/`basename source`. This converts `mv x y/' to `mv x y/x'.
|
|
||||||
This change means that the command `mv any file/' will now fail
|
|
||||||
rather than performing the move. The case when SOURCE is a
|
|
||||||
directory and DEST is not is properly diagnosed by do_move. */
|
|
||||||
|
|
||||||
if (dest_is_dir || (dest_had_trailing_slash && !is_real_dir (source)))
|
|
||||||
{
|
|
||||||
/* DEST is a directory; build full target filename. */
|
|
||||||
char *src_basename;
|
|
||||||
char *new_dest;
|
|
||||||
|
|
||||||
/* Remove trailing slashes before taking base_name.
|
|
||||||
Otherwise, base_name ("a/") returns "". */
|
|
||||||
strip_trailing_slashes_2 (source);
|
|
||||||
|
|
||||||
src_basename = base_name (source);
|
|
||||||
new_dest = path_concat (dest, src_basename, NULL);
|
|
||||||
if (new_dest == NULL)
|
|
||||||
xalloc_die ();
|
|
||||||
fail = do_move (source, new_dest, x);
|
|
||||||
|
|
||||||
/* Do not free new_dest. It may have been squirreled away by
|
|
||||||
the remember_copied function. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fail = do_move (source, dest, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("\
|
|
||||||
Usage: %s [OPTION]... SOURCE DEST\n\
|
|
||||||
or: %s [OPTION]... SOURCE... DIRECTORY\n\
|
|
||||||
or: %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name, program_name);
|
|
||||||
printf (_("\
|
|
||||||
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
|
|
||||||
\n\
|
|
||||||
--backup[=CONTROL] make a backup of each existing destination file\n\
|
|
||||||
-b like --backup but does not accept an argument\n\
|
|
||||||
-f, --force never prompt before overwriting\n\
|
|
||||||
-i, --interactive prompt before overwrite\n\
|
|
||||||
--strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
|
|
||||||
argument\n\
|
|
||||||
-S, --suffix=SUFFIX override the usual backup suffix\n\
|
|
||||||
--target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY\n\
|
|
||||||
-u, --update move only older or brand new non-directories\n\
|
|
||||||
-v, --verbose explain what is being done\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
"));
|
|
||||||
printf (_("\
|
|
||||||
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\
|
|
||||||
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\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
int errors;
|
|
||||||
int make_backups = 0;
|
|
||||||
int dest_is_dir;
|
|
||||||
char *backup_suffix_string;
|
|
||||||
char *version_control_string = NULL;
|
|
||||||
struct cp_options x;
|
|
||||||
char *target_directory = NULL;
|
|
||||||
int target_directory_specified;
|
|
||||||
unsigned int n_files;
|
|
||||||
char **file;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
cp_option_init (&x);
|
|
||||||
|
|
||||||
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
|
|
||||||
we'll actually use backup_suffix_string. */
|
|
||||||
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
|
|
||||||
|
|
||||||
errors = 0;
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'V': /* FIXME: this is deprecated. Remove it in 2001. */
|
|
||||||
error (0, 0,
|
|
||||||
_("warning: --version-control (-V) is obsolete; support for\
|
|
||||||
it\nwill be removed in some future release. Use --backup=%s instead."
|
|
||||||
), optarg);
|
|
||||||
/* Fall through. */
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
make_backups = 1;
|
|
||||||
if (optarg)
|
|
||||||
version_control_string = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
x.interactive = 0;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
x.interactive = 1;
|
|
||||||
break;
|
|
||||||
case STRIP_TRAILING_SLASHES_OPTION:
|
|
||||||
remove_trailing_slashes = 1;
|
|
||||||
break;
|
|
||||||
case TARGET_DIRECTORY_OPTION:
|
|
||||||
target_directory = optarg;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
x.update = 1;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
x.verbose = 1;
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
make_backups = 1;
|
|
||||||
backup_suffix_string = optarg;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n_files = argc - optind;
|
|
||||||
file = argv + optind;
|
|
||||||
|
|
||||||
target_directory_specified = (target_directory != NULL);
|
|
||||||
if (target_directory == NULL && n_files != 0)
|
|
||||||
target_directory = file[n_files - 1];
|
|
||||||
|
|
||||||
dest_is_dir = (n_files > 0 && isdir (target_directory));
|
|
||||||
|
|
||||||
if (n_files == 0 || (n_files == 1 && !target_directory_specified))
|
|
||||||
{
|
|
||||||
error (0, 0, _("missing file argument"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target_directory_specified)
|
|
||||||
{
|
|
||||||
if (!dest_is_dir)
|
|
||||||
{
|
|
||||||
error (0, 0, _("specified target, %s is not a directory"),
|
|
||||||
quote (target_directory));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (n_files > 2 && !dest_is_dir)
|
|
||||||
{
|
|
||||||
error (0, 0,
|
|
||||||
_("when moving multiple files, last argument must be a directory"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backup_suffix_string)
|
|
||||||
simple_backup_suffix = xstrdup (backup_suffix_string);
|
|
||||||
|
|
||||||
x.backup_type = (make_backups
|
|
||||||
? xget_version (_("backup type"),
|
|
||||||
version_control_string)
|
|
||||||
: none);
|
|
||||||
|
|
||||||
/* Move each arg but the last into the target_directory. */
|
|
||||||
{
|
|
||||||
unsigned int last_file_idx = (target_directory_specified
|
|
||||||
? n_files - 1
|
|
||||||
: n_files - 2);
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i <= last_file_idx; ++i)
|
|
||||||
errors |= movefile (file[i], target_directory, dest_is_dir, &x);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors);
|
|
||||||
}
|
|
||||||
@@ -1,946 +0,0 @@
|
|||||||
/* remove.c -- core functions for removing files and directories
|
|
||||||
Copyright (C) 88, 90, 91, 1994-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Extracted from rm.c and librarified by Jim Meyering. */
|
|
||||||
|
|
||||||
#ifdef _AIX
|
|
||||||
#pragma alloca
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#if HAVE_STDBOOL_H
|
|
||||||
# include <stdbool.h>
|
|
||||||
#else
|
|
||||||
typedef enum {false = 0, true = 1} bool;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "save-cwd.h"
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "obstack.h"
|
|
||||||
#include "hash.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "remove.h"
|
|
||||||
|
|
||||||
#define obstack_chunk_alloc malloc
|
|
||||||
#define obstack_chunk_free free
|
|
||||||
|
|
||||||
#ifndef PARAMS
|
|
||||||
# if defined (__GNUC__) || __STDC__
|
|
||||||
# define PARAMS(args) args
|
|
||||||
# else
|
|
||||||
# define PARAMS(args) ()
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* On systems with an lstat function that accepts the empty string,
|
|
||||||
arrange to make lstat calls go through the wrapper function. */
|
|
||||||
#if HAVE_LSTAT_EMPTY_STRING_BUG
|
|
||||||
int rpl_lstat PARAMS((const char *, struct stat *));
|
|
||||||
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef D_INO_IN_DIRENT
|
|
||||||
# define D_INO(dp) ((dp)->d_ino)
|
|
||||||
# define ENABLE_CYCLE_CHECK
|
|
||||||
#else
|
|
||||||
/* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
|
|
||||||
# define D_INO(dp) 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined S_ISLNK
|
|
||||||
# define S_ISLNK(Mode) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Initial capacity of per-directory hash table of entries that have
|
|
||||||
been processed but not been deleted. */
|
|
||||||
#define HT_INITIAL_CAPACITY 13
|
|
||||||
|
|
||||||
/* Initial capacity of the active directory hash table. This table will
|
|
||||||
be resized only for hierarchies more than about 45 levels deep. */
|
|
||||||
#define ACTIVE_DIR_INITIAL_CAPACITY 53
|
|
||||||
|
|
||||||
int euidaccess ();
|
|
||||||
int yesno ();
|
|
||||||
|
|
||||||
extern char *program_name;
|
|
||||||
|
|
||||||
/* state initialized by remove_init, freed by remove_fini */
|
|
||||||
|
|
||||||
/* An entry in the active_dir_map. */
|
|
||||||
struct active_dir_ent
|
|
||||||
{
|
|
||||||
ino_t st_ino;
|
|
||||||
dev_t st_dev;
|
|
||||||
unsigned int depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The name of the directory (starting with and relative to a command
|
|
||||||
line argument) being processed. When a subdirectory is entered, a new
|
|
||||||
component is appended (pushed). When RM chdir's out of a directory,
|
|
||||||
the top component is removed (popped). This is used to form a full
|
|
||||||
file name when necessary. */
|
|
||||||
static struct obstack dir_stack;
|
|
||||||
|
|
||||||
/* Stack of lengths of directory names (including trailing slash)
|
|
||||||
appended to dir_stack. We have to have a separate stack of lengths
|
|
||||||
(rather than just popping back to previous slash) because the first
|
|
||||||
element pushed onto the dir stack may contain slashes. */
|
|
||||||
static struct obstack len_stack;
|
|
||||||
|
|
||||||
/* Set of `active' directories from the current command-line argument
|
|
||||||
to the level in the hierarchy at which files are being removed.
|
|
||||||
A directory is added to the active set when RM begins removing it
|
|
||||||
(or its entries), and it is removed from the set just after RM has
|
|
||||||
finished processing it.
|
|
||||||
|
|
||||||
This is actually a map (not a set), implemented with a hash table.
|
|
||||||
For each active directory, it maps the directory's inode number to the
|
|
||||||
depth of that directory relative to the root of the tree being deleted.
|
|
||||||
A directory specified on the command line has depth zero.
|
|
||||||
This construct is used to detect directory cycles so that RM can warn
|
|
||||||
about them rather than iterating endlessly. */
|
|
||||||
#ifdef ENABLE_CYCLE_CHECK
|
|
||||||
static struct hash_table *active_dir_map;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline unsigned int
|
|
||||||
current_depth (void)
|
|
||||||
{
|
|
||||||
return obstack_object_size (&len_stack) / sizeof (size_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_nth_dir (FILE *stream, unsigned int depth)
|
|
||||||
{
|
|
||||||
size_t *length = (size_t *) obstack_base (&len_stack);
|
|
||||||
char *dir_name = (char *) obstack_base (&dir_stack);
|
|
||||||
unsigned int sum = 0;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
assert (depth < current_depth ());
|
|
||||||
|
|
||||||
for (i = 0; i <= depth; i++)
|
|
||||||
{
|
|
||||||
sum += length[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite (dir_name, 1, sum - 1, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct active_dir_ent *
|
|
||||||
make_active_dir_ent (ino_t inum, dev_t device, unsigned int depth)
|
|
||||||
{
|
|
||||||
struct active_dir_ent *ent;
|
|
||||||
ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
|
|
||||||
ent->st_ino = inum;
|
|
||||||
ent->st_dev = device;
|
|
||||||
ent->depth = depth;
|
|
||||||
return ent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
hash_active_dir_ent (void const *x, unsigned int table_size)
|
|
||||||
{
|
|
||||||
struct active_dir_ent const *ade = x;
|
|
||||||
|
|
||||||
/* Ignoring the device number here should be fine. */
|
|
||||||
return ade->st_ino % table_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
hash_compare_active_dir_ents (void const *x, void const *y)
|
|
||||||
{
|
|
||||||
struct active_dir_ent const *a = x;
|
|
||||||
struct active_dir_ent const *b = y;
|
|
||||||
return SAME_INODE (*a, *b) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A hash function for null-terminated char* strings using
|
|
||||||
the method described in Aho, Sethi, & Ullman, p 436. */
|
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
hash_pjw (const void *x, unsigned int tablesize)
|
|
||||||
{
|
|
||||||
const char *s = x;
|
|
||||||
unsigned int h = 0;
|
|
||||||
unsigned int g;
|
|
||||||
|
|
||||||
while (*s != 0)
|
|
||||||
{
|
|
||||||
h = (h << 4) + *s++;
|
|
||||||
if ((g = h & (unsigned int) 0xf0000000) != 0)
|
|
||||||
h = (h ^ (g >> 24)) ^ g;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (h % tablesize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
hash_compare_strings (void const *x, void const *y)
|
|
||||||
{
|
|
||||||
return STREQ (x, y) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
push_dir (const char *dir_name)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
len = strlen (dir_name);
|
|
||||||
|
|
||||||
/* Append the string onto the stack. */
|
|
||||||
obstack_grow (&dir_stack, dir_name, len);
|
|
||||||
|
|
||||||
/* Append a trailing slash. */
|
|
||||||
obstack_1grow (&dir_stack, '/');
|
|
||||||
|
|
||||||
/* Add one for the slash. */
|
|
||||||
++len;
|
|
||||||
|
|
||||||
/* Push the length (including slash) onto its stack. */
|
|
||||||
obstack_grow (&len_stack, &len, sizeof (len));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
pop_dir (void)
|
|
||||||
{
|
|
||||||
int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
|
|
||||||
size_t *length = (size_t *) obstack_base (&len_stack);
|
|
||||||
size_t top_len;
|
|
||||||
|
|
||||||
assert (n_lengths > 0);
|
|
||||||
top_len = length[n_lengths - 1];
|
|
||||||
assert (top_len >= 2);
|
|
||||||
|
|
||||||
/* Pop off the specified length of pathname. */
|
|
||||||
assert (obstack_object_size (&dir_stack) >= top_len);
|
|
||||||
obstack_blank (&dir_stack, -top_len);
|
|
||||||
|
|
||||||
/* Pop the length stack, too. */
|
|
||||||
assert (obstack_object_size (&len_stack) >= sizeof (size_t));
|
|
||||||
obstack_blank (&len_stack, (int) -(sizeof (size_t)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
|
|
||||||
buffer, DST, so that the last source byte is at the end of the destination
|
|
||||||
buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
|
|
||||||
Set *RESULT to point to the beginning of (the portion of) the source data
|
|
||||||
in DST. Return the number of bytes remaining in the destination buffer. */
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
|
|
||||||
char **result, int *truncated)
|
|
||||||
{
|
|
||||||
const char *sp;
|
|
||||||
char *dp;
|
|
||||||
|
|
||||||
if (src_len <= dst_len)
|
|
||||||
{
|
|
||||||
sp = src;
|
|
||||||
dp = dst + (dst_len - src_len);
|
|
||||||
*truncated = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sp = src + (src_len - dst_len);
|
|
||||||
dp = dst;
|
|
||||||
src_len = dst_len;
|
|
||||||
*truncated = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = memcpy (dp, sp, src_len);
|
|
||||||
return dst_len - src_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Using the global directory name obstack, create the full path to FILENAME.
|
|
||||||
Return it in sometimes-realloc'd space that should not be freed by the
|
|
||||||
caller. Realloc as necessary. If realloc fails, use a static buffer
|
|
||||||
and put as long a suffix in that buffer as possible. */
|
|
||||||
|
|
||||||
static char *
|
|
||||||
full_filename (const char *filename)
|
|
||||||
{
|
|
||||||
static char *buf = NULL;
|
|
||||||
static size_t n_allocated = 0;
|
|
||||||
|
|
||||||
int dir_len = obstack_object_size (&dir_stack);
|
|
||||||
char *dir_name = (char *) obstack_base (&dir_stack);
|
|
||||||
size_t n_bytes_needed;
|
|
||||||
size_t filename_len;
|
|
||||||
|
|
||||||
filename_len = strlen (filename);
|
|
||||||
n_bytes_needed = dir_len + filename_len + 1;
|
|
||||||
|
|
||||||
if (n_bytes_needed > n_allocated)
|
|
||||||
{
|
|
||||||
/* This code requires that realloc accept NULL as the first arg.
|
|
||||||
This function must not use xrealloc. Otherwise, an out-of-memory
|
|
||||||
error involving a file name to be expanded here wouldn't ever
|
|
||||||
be issued. Use realloc and fall back on using a static buffer
|
|
||||||
if memory allocation fails. */
|
|
||||||
buf = realloc (buf, n_bytes_needed);
|
|
||||||
n_allocated = n_bytes_needed;
|
|
||||||
|
|
||||||
if (buf == NULL)
|
|
||||||
{
|
|
||||||
#define SBUF_SIZE 512
|
|
||||||
#define ELLIPSES_PREFIX "[...]"
|
|
||||||
static char static_buf[SBUF_SIZE];
|
|
||||||
int truncated;
|
|
||||||
size_t len;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
len = right_justify (static_buf, SBUF_SIZE, filename,
|
|
||||||
filename_len + 1, &p, &truncated);
|
|
||||||
right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
|
|
||||||
if (truncated)
|
|
||||||
{
|
|
||||||
memcpy (static_buf, ELLIPSES_PREFIX,
|
|
||||||
sizeof (ELLIPSES_PREFIX) - 1);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy directory part, including trailing slash, and then
|
|
||||||
append the filename part, including a trailing zero byte. */
|
|
||||||
memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
|
|
||||||
|
|
||||||
assert (strlen (buf) + 1 == n_bytes_needed);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
fspec_init_common (struct File_spec *fs)
|
|
||||||
{
|
|
||||||
fs->have_full_mode = 0;
|
|
||||||
fs->have_filetype_mode = 0;
|
|
||||||
fs->have_device = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
fspec_init_file (struct File_spec *fs, const char *filename)
|
|
||||||
{
|
|
||||||
fs->filename = (char *) filename;
|
|
||||||
fspec_init_common (fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
fspec_init_dp (struct File_spec *fs, struct dirent *dp)
|
|
||||||
{
|
|
||||||
fs->filename = dp->d_name;
|
|
||||||
fspec_init_common (fs);
|
|
||||||
fs->st_ino = D_INO (dp);
|
|
||||||
|
|
||||||
#if D_TYPE_IN_DIRENT && defined DT_UNKNOWN && defined DTTOIF
|
|
||||||
if (dp->d_type != DT_UNKNOWN)
|
|
||||||
{
|
|
||||||
fs->have_filetype_mode = 1;
|
|
||||||
fs->mode = DTTOIF (dp->d_type);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
fspec_get_full_mode (struct File_spec *fs)
|
|
||||||
{
|
|
||||||
struct stat stat_buf;
|
|
||||||
|
|
||||||
if (fs->have_full_mode)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (lstat (fs->filename, &stat_buf))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
fs->have_full_mode = 1;
|
|
||||||
fs->have_filetype_mode = 1;
|
|
||||||
fs->mode = stat_buf.st_mode;
|
|
||||||
fs->st_ino = stat_buf.st_ino;
|
|
||||||
fs->have_device = 1;
|
|
||||||
fs->st_dev = stat_buf.st_dev;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
fspec_get_device_number (struct File_spec *fs)
|
|
||||||
{
|
|
||||||
struct stat stat_buf;
|
|
||||||
|
|
||||||
if (fs->have_device)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (lstat (fs->filename, &stat_buf))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
fs->have_full_mode = 1;
|
|
||||||
fs->have_filetype_mode = 1;
|
|
||||||
fs->mode = stat_buf.st_mode;
|
|
||||||
fs->st_ino = stat_buf.st_ino;
|
|
||||||
fs->have_device = 1;
|
|
||||||
fs->st_dev = stat_buf.st_dev;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
|
|
||||||
{
|
|
||||||
int fail;
|
|
||||||
|
|
||||||
fail = fs->have_filetype_mode ? 0 : fspec_get_full_mode (fs);
|
|
||||||
if (!fail)
|
|
||||||
*filetype_mode = fs->mode;
|
|
||||||
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline mode_t
|
|
||||||
fspec_filetype_mode (const struct File_spec *fs)
|
|
||||||
{
|
|
||||||
assert (fs->have_filetype_mode);
|
|
||||||
return fs->mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
same_file (const char *file_1, const char *file_2)
|
|
||||||
{
|
|
||||||
struct stat sb1, sb2;
|
|
||||||
return (lstat (file_1, &sb1) == 0
|
|
||||||
&& lstat (file_2, &sb2) == 0
|
|
||||||
&& SAME_INODE (sb1, sb2));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Recursively remove all of the entries in the current directory.
|
|
||||||
Return an indication of the success of the operation. */
|
|
||||||
|
|
||||||
static enum RM_status
|
|
||||||
remove_cwd_entries (const struct rm_options *x)
|
|
||||||
{
|
|
||||||
/* NOTE: this is static. */
|
|
||||||
static DIR *dirp = NULL;
|
|
||||||
|
|
||||||
/* NULL or a malloc'd and initialized hash table of entries in the
|
|
||||||
current directory that have been processed but not removed --
|
|
||||||
due either to an error or to an interactive `no' response. */
|
|
||||||
struct hash_table *ht = NULL;
|
|
||||||
|
|
||||||
/* FIXME: describe */
|
|
||||||
static struct obstack entry_name_pool;
|
|
||||||
static int first_call = 1;
|
|
||||||
|
|
||||||
enum RM_status status = RM_OK;
|
|
||||||
|
|
||||||
if (first_call)
|
|
||||||
{
|
|
||||||
first_call = 0;
|
|
||||||
obstack_init (&entry_name_pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirp)
|
|
||||||
{
|
|
||||||
if (CLOSEDIR (dirp))
|
|
||||||
{
|
|
||||||
/* FIXME-someday: but this is actually the previously opened dir. */
|
|
||||||
error (0, errno, "%s", quote (full_filename (".")));
|
|
||||||
status = RM_ERROR;
|
|
||||||
}
|
|
||||||
dirp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
/* FIXME: why do this? */
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
dirp = opendir (".");
|
|
||||||
if (dirp == NULL)
|
|
||||||
{
|
|
||||||
if (errno != ENOENT || !x->ignore_missing_files)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot open directory %s"),
|
|
||||||
quote (full_filename (".")));
|
|
||||||
status = RM_ERROR;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
char *entry_name;
|
|
||||||
struct File_spec fs;
|
|
||||||
enum RM_status tmp_status;
|
|
||||||
struct dirent *dp;
|
|
||||||
|
|
||||||
/* FILE should be skipped if it is `.' or `..', or if it is in
|
|
||||||
the table, HT, of entries we've already processed. */
|
|
||||||
#define SKIPPABLE(Ht, File) \
|
|
||||||
(DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
|
|
||||||
|
|
||||||
/* FIXME: use readdir_r directly into an obstack to avoid
|
|
||||||
the obstack_copy0 below --
|
|
||||||
Suggestion from Uli. Be careful -- there are different
|
|
||||||
prototypes on e.g. Solaris.
|
|
||||||
|
|
||||||
Do something like this:
|
|
||||||
#define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
|
|
||||||
_PC_NAME_MAX);
|
|
||||||
dp = obstack_alloc (sizeof (struct dirent)
|
|
||||||
+ NAME_MAX_FOR (".") + 1);
|
|
||||||
fail = xreaddir (dirp, dp);
|
|
||||||
where xreaddir is ...
|
|
||||||
|
|
||||||
But what about systems like the hurd where NAME_MAX is supposed
|
|
||||||
to be effectively unlimited. We don't want to have to allocate
|
|
||||||
a huge buffer to accommodate maximum possible entry name. */
|
|
||||||
|
|
||||||
dp = readdir (dirp);
|
|
||||||
|
|
||||||
#if ! HAVE_WORKING_READDIR
|
|
||||||
if (dp == NULL)
|
|
||||||
{
|
|
||||||
/* Since we have probably modified the directory since it
|
|
||||||
was opened, readdir returning NULL does not necessarily
|
|
||||||
mean we have read the last entry. Rewind it and check
|
|
||||||
again. This happens on SunOS4.1.4 with 254 or more files
|
|
||||||
in a directory. */
|
|
||||||
rewinddir (dirp);
|
|
||||||
while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
|
|
||||||
{
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (dp == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (SKIPPABLE (ht, dp->d_name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fspec_init_dp (&fs, dp);
|
|
||||||
|
|
||||||
/* Save a copy of the name of this entry, in case we have
|
|
||||||
to add it to the set of unremoved entries below. */
|
|
||||||
entry_name = obstack_copy0 (&entry_name_pool,
|
|
||||||
dp->d_name, NLENGTH (dp));
|
|
||||||
|
|
||||||
/* CAUTION: after this call to rm, DP may not be valid --
|
|
||||||
it may have been freed due to a close in a recursive call
|
|
||||||
(through rm and remove_dir) to this function. */
|
|
||||||
tmp_status = rm (&fs, 0, x);
|
|
||||||
|
|
||||||
/* Update status. */
|
|
||||||
if (tmp_status > status)
|
|
||||||
status = tmp_status;
|
|
||||||
assert (VALID_STATUS (status));
|
|
||||||
|
|
||||||
/* If this entry was not removed (due either to an error or to
|
|
||||||
an interactive `no' response), record it in the hash table so
|
|
||||||
we don't consider it again if we reopen this directory later. */
|
|
||||||
if (status != RM_OK)
|
|
||||||
{
|
|
||||||
if (ht == NULL)
|
|
||||||
{
|
|
||||||
ht = hash_initialize (HT_INITIAL_CAPACITY, NULL, hash_pjw,
|
|
||||||
hash_compare_strings, NULL);
|
|
||||||
if (ht == NULL)
|
|
||||||
xalloc_die ();
|
|
||||||
}
|
|
||||||
if (! hash_insert (ht, entry_name))
|
|
||||||
xalloc_die ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* This entry was not saved in the hash table. Free it. */
|
|
||||||
obstack_free (&entry_name_pool, entry_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirp == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (dirp == NULL);
|
|
||||||
|
|
||||||
if (dirp)
|
|
||||||
{
|
|
||||||
if (CLOSEDIR (dirp))
|
|
||||||
{
|
|
||||||
error (0, errno, _("closing directory %s"),
|
|
||||||
quote (full_filename (".")));
|
|
||||||
status = RM_ERROR;
|
|
||||||
}
|
|
||||||
dirp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ht)
|
|
||||||
{
|
|
||||||
hash_free (ht);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obstack_object_size (&entry_name_pool) > 0)
|
|
||||||
obstack_free (&entry_name_pool, obstack_base (&entry_name_pool));
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Query the user if appropriate, and if ok try to remove the
|
|
||||||
file or directory specified by FS. Return RM_OK if it is removed,
|
|
||||||
and RM_ERROR or RM_USER_DECLINED if not. */
|
|
||||||
|
|
||||||
static enum RM_status
|
|
||||||
remove_file (struct File_spec *fs, const struct rm_options *x)
|
|
||||||
{
|
|
||||||
int asked = 0;
|
|
||||||
char *pathname = fs->filename;
|
|
||||||
|
|
||||||
if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
|
|
||||||
&& euidaccess (pathname, W_OK))
|
|
||||||
{
|
|
||||||
if (!S_ISLNK (fspec_filetype_mode (fs)))
|
|
||||||
{
|
|
||||||
fprintf (stderr,
|
|
||||||
(S_ISDIR (fspec_filetype_mode (fs))
|
|
||||||
? _("%s: remove write-protected directory %s? ")
|
|
||||||
: _("%s: remove write-protected file %s? ")),
|
|
||||||
program_name, quote (full_filename (pathname)));
|
|
||||||
if (!yesno ())
|
|
||||||
return RM_USER_DECLINED;
|
|
||||||
|
|
||||||
asked = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!asked && x->interactive)
|
|
||||||
{
|
|
||||||
/* FIXME: use a variant of error (instead of fprintf) that doesn't
|
|
||||||
append a newline. Then we won't have to declare program_name in
|
|
||||||
this file. */
|
|
||||||
fprintf (stderr,
|
|
||||||
(S_ISDIR (fspec_filetype_mode (fs))
|
|
||||||
? _("%s: remove directory %s? ")
|
|
||||||
: _("%s: remove %s? ")),
|
|
||||||
program_name, quote (full_filename (pathname)));
|
|
||||||
if (!yesno ())
|
|
||||||
return RM_USER_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->verbose)
|
|
||||||
printf (_("removing %s\n"), quote (full_filename (pathname)));
|
|
||||||
|
|
||||||
if (unlink (pathname) && (errno != ENOENT || !x->ignore_missing_files))
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot unlink %s"), quote (full_filename (pathname)));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
return RM_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not in recursive mode, print an error message and return RM_ERROR.
|
|
||||||
Otherwise, query the user if appropriate, then try to recursively
|
|
||||||
remove the directory specified by FS. Return RM_OK if it is removed,
|
|
||||||
and RM_ERROR or RM_USER_DECLINED if not.
|
|
||||||
FIXME: describe need_save_cwd parameter. */
|
|
||||||
|
|
||||||
static enum RM_status
|
|
||||||
remove_dir (struct File_spec *fs, int need_save_cwd, const struct rm_options *x)
|
|
||||||
{
|
|
||||||
enum RM_status status;
|
|
||||||
struct saved_cwd cwd;
|
|
||||||
char *dir_name = fs->filename;
|
|
||||||
const char *fmt = NULL;
|
|
||||||
|
|
||||||
if (!x->recursive)
|
|
||||||
{
|
|
||||||
error (0, 0, _("%s is a directory"), quote (full_filename (dir_name)));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
|
|
||||||
&& euidaccess (dir_name, W_OK))
|
|
||||||
{
|
|
||||||
fmt = _("%s: directory %s is write protected; descend into it anyway? ");
|
|
||||||
}
|
|
||||||
else if (x->interactive)
|
|
||||||
{
|
|
||||||
fmt = _("%s: descend into directory %s? ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fmt)
|
|
||||||
{
|
|
||||||
fprintf (stderr, fmt, program_name, quote (full_filename (dir_name)));
|
|
||||||
if (!yesno ())
|
|
||||||
return RM_USER_DECLINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->verbose)
|
|
||||||
printf (_("removing all entries of directory %s\n"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
|
|
||||||
/* Save cwd if needed. */
|
|
||||||
if (need_save_cwd && save_cwd (&cwd))
|
|
||||||
return RM_ERROR;
|
|
||||||
|
|
||||||
/* Make target directory the current one. */
|
|
||||||
if (chdir (dir_name) < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot change to directory %s"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
if (need_save_cwd)
|
|
||||||
free_cwd (&cwd);
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verify that the device and inode numbers of `.' are the same as
|
|
||||||
the ones we recorded for dir_name before we cd'd into it. This
|
|
||||||
detects the scenario in which an attacker tries to make Bob's rm
|
|
||||||
command remove some other directory belonging to Bob. The method
|
|
||||||
would be to replace an existing lstat'd but-not-yet-removed directory
|
|
||||||
with a symlink to the target directory. */
|
|
||||||
{
|
|
||||||
struct stat sb;
|
|
||||||
if (lstat (".", &sb))
|
|
||||||
error (EXIT_FAILURE, errno,
|
|
||||||
_("cannot lstat `.' in %s"), quote (full_filename (dir_name)));
|
|
||||||
|
|
||||||
assert (fs->have_device);
|
|
||||||
if (!SAME_INODE (sb, *fs))
|
|
||||||
{
|
|
||||||
error (EXIT_FAILURE, 0,
|
|
||||||
_("ERROR: the directory %s initially had device/inode\n\
|
|
||||||
numbers %lu/%lu, but now (after a chdir into it), the numbers for `.'\n\
|
|
||||||
are %lu/%lu. That means that while rm was running, the directory\n\
|
|
||||||
was replaced with either another directory or a link to another directory."),
|
|
||||||
quote (full_filename (dir_name)),
|
|
||||||
(unsigned long)(fs->st_dev),
|
|
||||||
(unsigned long)(fs->st_ino),
|
|
||||||
(unsigned long)(sb.st_dev),
|
|
||||||
(unsigned long)(sb.st_ino));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push_dir (dir_name);
|
|
||||||
|
|
||||||
/* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
|
|
||||||
it because it is just a pointer to the dir entry's d_name field, and
|
|
||||||
remove_cwd_entries may close the directory. */
|
|
||||||
ASSIGN_STRDUPA (dir_name, dir_name);
|
|
||||||
|
|
||||||
status = remove_cwd_entries (x);
|
|
||||||
|
|
||||||
pop_dir ();
|
|
||||||
|
|
||||||
/* Restore cwd. */
|
|
||||||
if (need_save_cwd)
|
|
||||||
{
|
|
||||||
if (restore_cwd (&cwd, NULL, NULL))
|
|
||||||
{
|
|
||||||
free_cwd (&cwd);
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
free_cwd (&cwd);
|
|
||||||
}
|
|
||||||
else if (chdir ("..") < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot change back to directory %s via `..'"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->interactive)
|
|
||||||
{
|
|
||||||
fprintf (stderr, _("%s: remove directory %s%s? "),
|
|
||||||
program_name,
|
|
||||||
quote (full_filename (dir_name)),
|
|
||||||
(status != RM_OK ? _(" (might be nonempty)") : ""));
|
|
||||||
if (!yesno ())
|
|
||||||
{
|
|
||||||
return RM_USER_DECLINED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x->verbose)
|
|
||||||
printf (_("removing the directory itself: %s\n"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
|
|
||||||
if (rmdir (dir_name) && (errno != ENOENT || !x->ignore_missing_files))
|
|
||||||
{
|
|
||||||
int saved_errno = errno;
|
|
||||||
|
|
||||||
#ifndef EINVAL
|
|
||||||
# define EINVAL 0
|
|
||||||
#endif
|
|
||||||
/* See if rmdir just failed because DIR_NAME is the current directory.
|
|
||||||
If so, give a better diagnostic than `rm: cannot remove directory
|
|
||||||
`...': Invalid argument' */
|
|
||||||
if (errno == EINVAL && same_file (".", dir_name))
|
|
||||||
{
|
|
||||||
error (0, 0, _("cannot remove current directory %s"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error (0, saved_errno, _("cannot remove directory %s"),
|
|
||||||
quote (full_filename (dir_name)));
|
|
||||||
}
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the file or directory specified by FS after checking appropriate
|
|
||||||
things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
|
|
||||||
if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
|
|
||||||
be `.', `..', or may contain slashes. Otherwise, it must be a simple file
|
|
||||||
name (and hence must specify a file in the current directory). */
|
|
||||||
|
|
||||||
enum RM_status
|
|
||||||
rm (struct File_spec *fs, int user_specified_name, const struct rm_options *x)
|
|
||||||
{
|
|
||||||
mode_t filetype_mode;
|
|
||||||
|
|
||||||
if (user_specified_name)
|
|
||||||
{
|
|
||||||
/* CAUTION: this use of base_name works only because any
|
|
||||||
trailing slashes in fs->filename have already been removed. */
|
|
||||||
char *base = base_name (fs->filename);
|
|
||||||
|
|
||||||
if (DOT_OR_DOTDOT (base))
|
|
||||||
{
|
|
||||||
error (0, 0, _("cannot remove `.' or `..'"));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fspec_get_filetype_mode (fs, &filetype_mode))
|
|
||||||
{
|
|
||||||
if (x->ignore_missing_files && errno == ENOENT)
|
|
||||||
return RM_OK;
|
|
||||||
|
|
||||||
error (0, errno, _("cannot remove %s"),
|
|
||||||
quote (full_filename (fs->filename)));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_CYCLE_CHECK
|
|
||||||
if (S_ISDIR (filetype_mode))
|
|
||||||
{
|
|
||||||
struct active_dir_ent *old_ent;
|
|
||||||
struct active_dir_ent *new_ent;
|
|
||||||
|
|
||||||
/* If there is already a directory in the map with the same device
|
|
||||||
and inode numbers, then there is a directory cycle. */
|
|
||||||
|
|
||||||
if (fspec_get_device_number (fs))
|
|
||||||
{
|
|
||||||
error (0, errno, _("cannot stat %s"),
|
|
||||||
quote (full_filename (fs->filename)));
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
new_ent = make_active_dir_ent (fs->st_ino, fs->st_dev, current_depth ());
|
|
||||||
old_ent = hash_lookup (active_dir_map, new_ent);
|
|
||||||
if (old_ent)
|
|
||||||
{
|
|
||||||
error (0, 0, _("\
|
|
||||||
WARNING: Circular directory structure.\n\
|
|
||||||
This almost certainly means that you have a corrupted file system.\n\
|
|
||||||
NOTIFY YOUR SYSTEM MANAGER.\n\
|
|
||||||
The following two directories have the same inode number:\n"));
|
|
||||||
print_nth_dir (stderr, old_ent->depth);
|
|
||||||
fprintf (stderr, "\n%s\n", quote (full_filename (fs->filename)));
|
|
||||||
fflush (stderr);
|
|
||||||
|
|
||||||
if (x->interactive)
|
|
||||||
{
|
|
||||||
error (0, 0, _("continue? "));
|
|
||||||
if (yesno ())
|
|
||||||
return RM_ERROR;
|
|
||||||
}
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Put this directory in the active_dir_map. */
|
|
||||||
if (! hash_insert (active_dir_map, new_ent))
|
|
||||||
xalloc_die ();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!S_ISDIR (filetype_mode) || x->unlink_dirs)
|
|
||||||
{
|
|
||||||
return remove_file (fs, x);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int need_save_cwd = user_specified_name;
|
|
||||||
enum RM_status status;
|
|
||||||
|
|
||||||
if (need_save_cwd)
|
|
||||||
need_save_cwd = (strchr (fs->filename, '/') != NULL);
|
|
||||||
|
|
||||||
status = remove_dir (fs, need_save_cwd, x);
|
|
||||||
|
|
||||||
#ifdef ENABLE_CYCLE_CHECK
|
|
||||||
{
|
|
||||||
struct active_dir_ent tmp;
|
|
||||||
struct active_dir_ent *old_ent;
|
|
||||||
|
|
||||||
/* Remove this directory from the active_dir_map. */
|
|
||||||
tmp.st_ino = fs->st_ino;
|
|
||||||
assert (fs->have_device);
|
|
||||||
tmp.st_dev = fs->st_dev;
|
|
||||||
old_ent = hash_delete (active_dir_map, &tmp);
|
|
||||||
assert (old_ent != NULL);
|
|
||||||
free (old_ent);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
remove_init (void)
|
|
||||||
{
|
|
||||||
/* Initialize dir-stack obstacks. */
|
|
||||||
obstack_init (&dir_stack);
|
|
||||||
obstack_init (&len_stack);
|
|
||||||
|
|
||||||
#ifdef ENABLE_CYCLE_CHECK
|
|
||||||
active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, NULL,
|
|
||||||
hash_active_dir_ent,
|
|
||||||
hash_compare_active_dir_ents, free);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
remove_fini (void)
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_CYCLE_CHECK
|
|
||||||
hash_free (active_dir_map);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
obstack_free (&dir_stack, NULL);
|
|
||||||
obstack_free (&len_stack, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
struct rm_options
|
|
||||||
{
|
|
||||||
/* If nonzero, ignore nonexistent files. */
|
|
||||||
int ignore_missing_files;
|
|
||||||
|
|
||||||
/* If nonzero, query the user about whether to remove each file. */
|
|
||||||
int interactive;
|
|
||||||
|
|
||||||
/* If nonzero, recursively remove directories. */
|
|
||||||
int recursive;
|
|
||||||
|
|
||||||
/* If nonzero, stdin is a tty. */
|
|
||||||
int stdin_tty;
|
|
||||||
|
|
||||||
/* If nonzero, remove directories with unlink instead of rmdir, and don't
|
|
||||||
require a directory to be empty before trying to unlink it.
|
|
||||||
Only works for the super-user. */
|
|
||||||
int unlink_dirs;
|
|
||||||
|
|
||||||
/* If nonzero, display the name of each file removed. */
|
|
||||||
int verbose;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RM_status
|
|
||||||
{
|
|
||||||
/* These must be listed in order of increasing seriousness. */
|
|
||||||
RM_OK = 1,
|
|
||||||
RM_USER_DECLINED,
|
|
||||||
RM_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
#define VALID_STATUS(S) \
|
|
||||||
((S) == RM_OK || (S) == RM_USER_DECLINED || (S) == RM_ERROR)
|
|
||||||
|
|
||||||
struct File_spec
|
|
||||||
{
|
|
||||||
char *filename;
|
|
||||||
unsigned int have_filetype_mode:1;
|
|
||||||
unsigned int have_full_mode:1;
|
|
||||||
unsigned int have_device:1;
|
|
||||||
mode_t mode;
|
|
||||||
ino_t st_ino;
|
|
||||||
dev_t st_dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RM_status rm PARAMS ((struct File_spec *fs, int user_specified_name,
|
|
||||||
const struct rm_options *x));
|
|
||||||
void fspec_init_file PARAMS ((struct File_spec *fs, const char *filename));
|
|
||||||
void remove_init PARAMS ((void));
|
|
||||||
void remove_fini PARAMS ((void));
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
/* `rm' file deletion utility for GNU.
|
|
||||||
Copyright (C) 88, 90, 91, 1994-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
|
|
||||||
Reworked to use chdir and hash tables by Jim Meyering. */
|
|
||||||
|
|
||||||
/* Implementation overview:
|
|
||||||
|
|
||||||
In the `usual' case, RM saves no state for directories it is processing.
|
|
||||||
When a removal fails (either due to an error or to an interactive `no'
|
|
||||||
reply), the failure is noted (see description of `ht' in remove.c's
|
|
||||||
remove_cwd_entries function) so that when/if the containing directory
|
|
||||||
is reopened, RM doesn't try to remove the entry again.
|
|
||||||
|
|
||||||
RM may delete arbitrarily deep hierarchies -- even ones in which file
|
|
||||||
names (from root to leaf) are longer than the system-imposed maximum.
|
|
||||||
It does this by using chdir to change to each directory in turn before
|
|
||||||
removing the entries in that directory.
|
|
||||||
|
|
||||||
RM detects directory cycles by maintaining a table of the currently
|
|
||||||
active directories. See the description of active_dir_map in remove.c.
|
|
||||||
|
|
||||||
RM is careful to avoid forming full file names whenever possible.
|
|
||||||
A full file name is formed only when it is about to be used -- e.g.
|
|
||||||
in a diagnostic or in an interactive-mode prompt.
|
|
||||||
|
|
||||||
RM minimizes the number of lstat system calls it makes. On systems
|
|
||||||
that have valid d_type data in directory entries, RM makes only one
|
|
||||||
lstat call per command line argument -- regardless of the depth of
|
|
||||||
the hierarchy. */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "remove.h"
|
|
||||||
#include "save-cwd.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "rm"
|
|
||||||
|
|
||||||
#define AUTHORS \
|
|
||||||
"Paul Rubin, David MacKenzie, Richard Stallman, and Jim Meyering"
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* Name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
static struct option const long_opts[] =
|
|
||||||
{
|
|
||||||
{"directory", no_argument, NULL, 'd'},
|
|
||||||
{"force", no_argument, NULL, 'f'},
|
|
||||||
{"interactive", no_argument, NULL, 'i'},
|
|
||||||
{"recursive", no_argument, NULL, 'r'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Remove (unlink) the FILE(s).\n\
|
|
||||||
\n\
|
|
||||||
-d, --directory unlink directory, even if non-empty (super-user only)\n\
|
|
||||||
-f, --force ignore nonexistent files, never prompt\n\
|
|
||||||
-i, --interactive prompt before any removal\n\
|
|
||||||
-r, -R, --recursive remove the contents of directories recursively\n\
|
|
||||||
-v, --verbose explain what is being done\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
To remove a file whose name starts with a `-', for example `-foo',\n\
|
|
||||||
use one of these commands:\n\
|
|
||||||
%s -- -foo\n\
|
|
||||||
\n\
|
|
||||||
%s ./-foo\n\
|
|
||||||
\n\
|
|
||||||
Note that if you use rm to remove a file, it is usually possible to recover\n\
|
|
||||||
the contents of that file. If you want more assurance that the contents are\n\
|
|
||||||
truly unrecoverable, consider using shred.\n\
|
|
||||||
"),
|
|
||||||
program_name, program_name);
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rm_option_init (struct rm_options *x)
|
|
||||||
{
|
|
||||||
x->unlink_dirs = 0;
|
|
||||||
x->ignore_missing_files = 0;
|
|
||||||
x->interactive = 0;
|
|
||||||
x->recursive = 0;
|
|
||||||
x->stdin_tty = isatty (STDIN_FILENO);
|
|
||||||
x->verbose = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct rm_options x;
|
|
||||||
int fail = 0;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
rm_option_init (&x);
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0: /* Long option. */
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
x.unlink_dirs = 1;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
x.interactive = 0;
|
|
||||||
x.ignore_missing_files = 1;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
x.interactive = 1;
|
|
||||||
x.ignore_missing_files = 0;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
case 'R':
|
|
||||||
x.recursive = 1;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
x.verbose = 1;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
if (x.ignore_missing_files)
|
|
||||||
exit (0);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_init ();
|
|
||||||
|
|
||||||
for (; optind < argc; optind++)
|
|
||||||
{
|
|
||||||
struct File_spec fs;
|
|
||||||
enum RM_status status;
|
|
||||||
|
|
||||||
/* Stripping slashes is harmless for rmdir;
|
|
||||||
if the arg is not a directory, it will fail with ENOTDIR. */
|
|
||||||
strip_trailing_slashes (argv[optind]);
|
|
||||||
fspec_init_file (&fs, argv[optind]);
|
|
||||||
status = rm (&fs, 1, &x);
|
|
||||||
assert (VALID_STATUS (status));
|
|
||||||
if (status == RM_ERROR)
|
|
||||||
fail = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_fini ();
|
|
||||||
|
|
||||||
exit (fail);
|
|
||||||
}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
/* rmdir -- remove directories
|
|
||||||
Copyright (C) 90, 91, 1995-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Options:
|
|
||||||
-p, --parent Remove any parent dirs that are explicitly mentioned
|
|
||||||
in an argument, if they become empty after the
|
|
||||||
argument file is removed.
|
|
||||||
|
|
||||||
David MacKenzie <[email protected]> */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "quote.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "rmdir"
|
|
||||||
|
|
||||||
#define AUTHORS "David MacKenzie"
|
|
||||||
|
|
||||||
#ifndef EEXIST
|
|
||||||
# define EEXIST 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ENOTEMPTY
|
|
||||||
# define ENOTEMPTY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void strip_trailing_slashes ();
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* If nonzero, remove empty parent directories. */
|
|
||||||
static int empty_paths;
|
|
||||||
|
|
||||||
/* If nonzero, don't treat failure to remove a nonempty directory
|
|
||||||
as an error. */
|
|
||||||
static int ignore_fail_on_non_empty;
|
|
||||||
|
|
||||||
/* If nonzero, output a diagnostic for every directory processed. */
|
|
||||||
static int verbose;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
IGNORE_FAIL_ON_NON_EMPTY_OPTION = CHAR_MAX + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const longopts[] =
|
|
||||||
{
|
|
||||||
/* Don't name this `--force' because it's not close enough in meaning
|
|
||||||
to e.g. rm's -f option. */
|
|
||||||
{"ignore-fail-on-non-empty", no_argument, NULL,
|
|
||||||
IGNORE_FAIL_ON_NON_EMPTY_OPTION},
|
|
||||||
|
|
||||||
{"path", no_argument, NULL, 'p'},
|
|
||||||
{"parents", no_argument, NULL, 'p'},
|
|
||||||
{"verbose", no_argument, NULL, 'v'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{NULL, 0, NULL, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Return nonzero if ERROR_NUMBER is one of the values associated
|
|
||||||
with a failed rmdir due to non-empty target directory. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
errno_rmdir_non_empty (int error_number)
|
|
||||||
{
|
|
||||||
return (error_number == RMDIR_ERRNO_NOT_EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove any empty parent directories of PATH.
|
|
||||||
If PATH contains slash characters, at least one of them
|
|
||||||
(beginning with the rightmost) is replaced with a NUL byte. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
remove_parents (char *path)
|
|
||||||
{
|
|
||||||
char *slash;
|
|
||||||
int fail = 0;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
slash = strrchr (path, '/');
|
|
||||||
if (slash == NULL)
|
|
||||||
break;
|
|
||||||
/* Remove any characters after the slash, skipping any extra
|
|
||||||
slashes in a row. */
|
|
||||||
while (slash > path && *slash == '/')
|
|
||||||
--slash;
|
|
||||||
slash[1] = 0;
|
|
||||||
|
|
||||||
/* Give a diagnostic for each attempted removal if --verbose. */
|
|
||||||
if (verbose)
|
|
||||||
error (0, 0, _("removing directory, %s"), path);
|
|
||||||
|
|
||||||
fail = rmdir (path);
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
/* Stop quietly if --ignore-fail-on-non-empty. */
|
|
||||||
if (ignore_fail_on_non_empty
|
|
||||||
&& errno_rmdir_non_empty (errno))
|
|
||||||
{
|
|
||||||
fail = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error (0, errno, "%s", quote (path));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Remove the DIRECTORY(ies), if they are empty.\n\
|
|
||||||
\n\
|
|
||||||
--ignore-fail-on-non-empty\n\
|
|
||||||
ignore each failure that is solely because a directory\n\
|
|
||||||
is non-empty\n\
|
|
||||||
-p, --parents remove DIRECTORY, then try to remove each directory\n\
|
|
||||||
component of that path name. E.g., `rmdir -p a/b/c' is\n\
|
|
||||||
similar to `rmdir a/b/c a/b a'.\n\
|
|
||||||
-v, --verbose output a diagnostic for every directory processed\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int errors = 0;
|
|
||||||
int optc;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
empty_paths = 0;
|
|
||||||
|
|
||||||
while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (optc)
|
|
||||||
{
|
|
||||||
case 0: /* Long option. */
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
empty_paths = 1;
|
|
||||||
break;
|
|
||||||
case IGNORE_FAIL_ON_NON_EMPTY_OPTION:
|
|
||||||
ignore_fail_on_non_empty = 1;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose = 1;
|
|
||||||
break;
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
error (0, 0, _("too few arguments"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
{
|
|
||||||
int fail;
|
|
||||||
char *dir = argv[optind];
|
|
||||||
|
|
||||||
/* Stripping slashes is harmless for rmdir;
|
|
||||||
if the arg is not a directory, it will fail with ENOTDIR. */
|
|
||||||
strip_trailing_slashes (dir);
|
|
||||||
|
|
||||||
/* Give a diagnostic for each attempted removal if --verbose. */
|
|
||||||
if (verbose)
|
|
||||||
error (0, 0, _("removing directory, %s"), dir);
|
|
||||||
|
|
||||||
fail = rmdir (dir);
|
|
||||||
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
if (ignore_fail_on_non_empty
|
|
||||||
&& errno_rmdir_non_empty (errno))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
error (0, errno, "%s", quote (dir));
|
|
||||||
errors = 1;
|
|
||||||
}
|
|
||||||
else if (empty_paths)
|
|
||||||
{
|
|
||||||
errors += remove_parents (dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/* sync - update the super block
|
|
||||||
Copyright (C) 1994-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by Jim Meyering */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "long-options.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "sync"
|
|
||||||
|
|
||||||
#define AUTHORS "Jim Meyering"
|
|
||||||
|
|
||||||
/* The name this program was run with. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]\n"), program_name);
|
|
||||||
printf (_("\
|
|
||||||
Force changed blocks to disk, update the super block.\n\
|
|
||||||
\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
|
|
||||||
AUTHORS, usage);
|
|
||||||
|
|
||||||
if (argc != 1)
|
|
||||||
error (0, 0, _("ignoring all arguments"));
|
|
||||||
|
|
||||||
sync ();
|
|
||||||
exit (0);
|
|
||||||
}
|
|
||||||
@@ -1,606 +0,0 @@
|
|||||||
/* WARNING -- this file is temporary. It is shared between the
|
|
||||||
sh-utils, fileutils, and textutils packages. Once I find a little
|
|
||||||
more time, I'll merge the remaining things in system.h and everything
|
|
||||||
in this file will go back there. */
|
|
||||||
|
|
||||||
#if STAT_MACROS_BROKEN
|
|
||||||
# undef S_ISBLK
|
|
||||||
# undef S_ISCHR
|
|
||||||
# undef S_ISDIR
|
|
||||||
# undef S_ISDOOR
|
|
||||||
# undef S_ISFIFO
|
|
||||||
# undef S_ISLNK
|
|
||||||
# undef S_ISMPB
|
|
||||||
# undef S_ISMPC
|
|
||||||
# undef S_ISNWK
|
|
||||||
# undef S_ISREG
|
|
||||||
# undef S_ISSOCK
|
|
||||||
#endif /* STAT_MACROS_BROKEN. */
|
|
||||||
|
|
||||||
#ifndef S_IFMT
|
|
||||||
# define S_IFMT 0170000
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISBLK) && defined(S_IFBLK)
|
|
||||||
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISCHR) && defined(S_IFCHR)
|
|
||||||
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISDIR) && defined(S_IFDIR)
|
|
||||||
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISREG) && defined(S_IFREG)
|
|
||||||
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISFIFO) && defined(S_IFIFO)
|
|
||||||
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISLNK) && defined(S_IFLNK)
|
|
||||||
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISSOCK) && defined(S_IFSOCK)
|
|
||||||
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
|
|
||||||
# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
|
|
||||||
# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
|
|
||||||
# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
|
|
||||||
#endif
|
|
||||||
#if !defined(S_ISDOOR) && defined(S_IFDOOR) /* Solaris 2.5 and up */
|
|
||||||
# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !S_ISUID
|
|
||||||
# define S_ISUID 04000
|
|
||||||
#endif
|
|
||||||
#if !S_ISGID
|
|
||||||
# define S_ISGID 02000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* S_ISVTX is a common extension to POSIX.1. */
|
|
||||||
#ifndef S_ISVTX
|
|
||||||
# define S_ISVTX 01000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !S_IRUSR && S_IREAD
|
|
||||||
# define S_IRUSR S_IREAD
|
|
||||||
#endif
|
|
||||||
#if !S_IRUSR
|
|
||||||
# define S_IRUSR 00400
|
|
||||||
#endif
|
|
||||||
#if !S_IRGRP
|
|
||||||
# define S_IRGRP (S_IRUSR >> 3)
|
|
||||||
#endif
|
|
||||||
#if !S_IROTH
|
|
||||||
# define S_IROTH (S_IRUSR >> 6)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !S_IWUSR && S_IWRITE
|
|
||||||
# define S_IWUSR S_IWRITE
|
|
||||||
#endif
|
|
||||||
#if !S_IWUSR
|
|
||||||
# define S_IWUSR 00200
|
|
||||||
#endif
|
|
||||||
#if !S_IWGRP
|
|
||||||
# define S_IWGRP (S_IWUSR >> 3)
|
|
||||||
#endif
|
|
||||||
#if !S_IWOTH
|
|
||||||
# define S_IWOTH (S_IWUSR >> 6)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !S_IXUSR && S_IEXEC
|
|
||||||
# define S_IXUSR S_IEXEC
|
|
||||||
#endif
|
|
||||||
#if !S_IXUSR
|
|
||||||
# define S_IXUSR 00100
|
|
||||||
#endif
|
|
||||||
#if !S_IXGRP
|
|
||||||
# define S_IXGRP (S_IXUSR >> 3)
|
|
||||||
#endif
|
|
||||||
#if !S_IXOTH
|
|
||||||
# define S_IXOTH (S_IXUSR >> 6)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !S_IRWXU
|
|
||||||
# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
|
|
||||||
#endif
|
|
||||||
#if !S_IRWXG
|
|
||||||
# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
|
|
||||||
#endif
|
|
||||||
#if !S_IRWXO
|
|
||||||
# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* S_IXUGO is a common extension to POSIX.1. */
|
|
||||||
#if !S_IXUGO
|
|
||||||
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef S_IRWXUGO
|
|
||||||
# define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* All the mode bits that can be affected by chmod. */
|
|
||||||
#define CHMOD_MODE_BITS \
|
|
||||||
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
|
|
||||||
|
|
||||||
#ifdef ST_MTIM_NSEC
|
|
||||||
# define ST_TIME_CMP_NS(a, b, ns) ((a).ns < (b).ns ? -1 : (a).ns > (b).ns)
|
|
||||||
#else
|
|
||||||
# define ST_TIME_CMP_NS(a, b, ns) 0
|
|
||||||
#endif
|
|
||||||
#define ST_TIME_CMP(a, b, s, ns) \
|
|
||||||
((a).s < (b).s ? -1 : (a).s > (b).s ? 1 : ST_TIME_CMP_NS(a, b, ns))
|
|
||||||
#define ATIME_CMP(a, b) ST_TIME_CMP (a, b, st_atime, st_atim.ST_MTIM_NSEC)
|
|
||||||
#define CTIME_CMP(a, b) ST_TIME_CMP (a, b, st_ctime, st_ctim.ST_MTIM_NSEC)
|
|
||||||
#define MTIME_CMP(a, b) ST_TIME_CMP (a, b, st_mtime, st_mtim.ST_MTIM_NSEC)
|
|
||||||
|
|
||||||
#ifndef RETSIGTYPE
|
|
||||||
# define RETSIGTYPE void
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __GNUC__
|
|
||||||
# if HAVE_ALLOCA_H
|
|
||||||
# include <alloca.h>
|
|
||||||
# else
|
|
||||||
# ifdef _AIX
|
|
||||||
# pragma alloca
|
|
||||||
# else
|
|
||||||
# ifdef _WIN32
|
|
||||||
# include <malloc.h>
|
|
||||||
# include <io.h>
|
|
||||||
# else
|
|
||||||
# ifndef alloca
|
|
||||||
char *alloca ();
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __DJGPP__
|
|
||||||
/* We need the declaration of setmode. */
|
|
||||||
# include <io.h>
|
|
||||||
/* We need the declaration of __djgpp_set_ctrl_c. */
|
|
||||||
# include <sys/exceptn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_STDINT_H
|
|
||||||
# include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_INTTYPES_H
|
|
||||||
# include <inttypes.h> /* for the definition of UINTMAX_MAX */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
/* Jim Meyering writes:
|
|
||||||
|
|
||||||
"... Some ctype macros are valid only for character codes that
|
|
||||||
isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
|
|
||||||
using /bin/cc or gcc but without giving an ansi option). So, all
|
|
||||||
ctype uses should be through macros like ISPRINT... If
|
|
||||||
STDC_HEADERS is defined, then autoconf has verified that the ctype
|
|
||||||
macros don't need to be guarded with references to isascii. ...
|
|
||||||
Defining isascii to 1 should let any compiler worth its salt
|
|
||||||
eliminate the && through constant folding."
|
|
||||||
|
|
||||||
Bruno Haible adds:
|
|
||||||
|
|
||||||
"... Furthermore, isupper(c) etc. have an undefined result if c is
|
|
||||||
outside the range -1 <= c <= 255. One is tempted to write isupper(c)
|
|
||||||
with c being of type `char', but this is wrong if c is an 8-bit
|
|
||||||
character >= 128 which gets sign-extended to a negative value.
|
|
||||||
The macro ISUPPER protects against this as well." */
|
|
||||||
|
|
||||||
#if STDC_HEADERS || (!defined (isascii) && !HAVE_ISASCII)
|
|
||||||
# define IN_CTYPE_DOMAIN(c) 1
|
|
||||||
#else
|
|
||||||
# define IN_CTYPE_DOMAIN(c) isascii(c)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef isblank
|
|
||||||
# define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
|
|
||||||
#else
|
|
||||||
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
|
|
||||||
#endif
|
|
||||||
#ifdef isgraph
|
|
||||||
# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
|
|
||||||
#else
|
|
||||||
# define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is defined in <sys/euc.h> on at least Solaris2.6 systems. */
|
|
||||||
#undef ISPRINT
|
|
||||||
|
|
||||||
#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
|
|
||||||
#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
|
|
||||||
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
|
|
||||||
#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
|
|
||||||
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
|
|
||||||
#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
|
|
||||||
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
|
|
||||||
#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
|
|
||||||
#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))
|
|
||||||
#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
|
|
||||||
|
|
||||||
#if STDC_HEADERS
|
|
||||||
# define TOLOWER(Ch) tolower (Ch)
|
|
||||||
# define TOUPPER(Ch) toupper (Ch)
|
|
||||||
#else
|
|
||||||
# define TOLOWER(Ch) (ISUPPER (Ch) ? tolower (Ch) : (Ch))
|
|
||||||
# define TOUPPER(Ch) (ISLOWER (Ch) ? toupper (Ch) : (Ch))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
|
|
||||||
- Its arg may be any int or unsigned int; it need not be an unsigned char.
|
|
||||||
- It's guaranteed to evaluate its argument exactly once.
|
|
||||||
- It's typically faster.
|
|
||||||
Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
|
|
||||||
only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
|
|
||||||
it's important to use the locale's definition of `digit' even when the
|
|
||||||
host does not conform to Posix. */
|
|
||||||
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
|
|
||||||
|
|
||||||
#ifndef PARAMS
|
|
||||||
# if PROTOTYPES
|
|
||||||
# define PARAMS(Args) Args
|
|
||||||
# else
|
|
||||||
# define PARAMS(Args) ()
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Take care of NLS matters. */
|
|
||||||
|
|
||||||
#if HAVE_LOCALE_H
|
|
||||||
# include <locale.h>
|
|
||||||
#endif
|
|
||||||
#if !HAVE_SETLOCALE
|
|
||||||
# define setlocale(Category, Locale) /* empty */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_NLS
|
|
||||||
# include <libintl.h>
|
|
||||||
# if HAVE_GETTEXT && !HAVE_DCGETTEXT && !defined dcgettext
|
|
||||||
# define dcgettext(Domain, Text, Category) Text
|
|
||||||
# endif
|
|
||||||
# define _(Text) gettext (Text)
|
|
||||||
#else
|
|
||||||
# undef bindtextdomain
|
|
||||||
# define bindtextdomain(Domain, Directory) /* empty */
|
|
||||||
# undef textdomain
|
|
||||||
# define textdomain(Domain) /* empty */
|
|
||||||
# undef dcgettext
|
|
||||||
# define dcgettext(Domainname, Text, Category) Text
|
|
||||||
# define _(Text) Text
|
|
||||||
#endif
|
|
||||||
#define N_(Text) Text
|
|
||||||
|
|
||||||
#define STREQ(a, b) (strcmp ((a), (b)) == 0)
|
|
||||||
|
|
||||||
#if !HAVE_DECL_FREE
|
|
||||||
void free ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_MALLOC
|
|
||||||
char *malloc ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_MEMCHR
|
|
||||||
char *memchr ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_REALLOC
|
|
||||||
char *realloc ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_STPCPY
|
|
||||||
# ifndef stpcpy
|
|
||||||
char *stpcpy ();
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_STRNDUP
|
|
||||||
char *strndup ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_STRSTR
|
|
||||||
char *strstr ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETENV
|
|
||||||
char *getenv ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_LSEEK
|
|
||||||
off_t lseek ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is needed on some AIX systems. */
|
|
||||||
#if !HAVE_DECL_STRTOUL
|
|
||||||
unsigned long strtoul ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is needed on some AIX systems. */
|
|
||||||
#if !HAVE_DECL_STRTOULL && HAVE_UNSIGNED_LONG_LONG
|
|
||||||
unsigned long long strtoull ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETLOGIN
|
|
||||||
char *getlogin ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_TTYNAME
|
|
||||||
char *ttyname ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETEUID
|
|
||||||
uid_t geteuid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETPWUID
|
|
||||||
struct passwd *getpwuid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETGRGID
|
|
||||||
struct group *getgrgid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !HAVE_DECL_GETUID
|
|
||||||
uid_t getuid ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "xalloc.h"
|
|
||||||
|
|
||||||
#if ! defined HAVE_MEMPCPY && ! defined mempcpy
|
|
||||||
/* Be CAREFUL that there are no side effects in N. */
|
|
||||||
# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* These are wrappers for functions/macros from GNU libc.
|
|
||||||
The standard I/O functions are thread-safe. These *_unlocked ones
|
|
||||||
are more efficient but not thread-safe. That they're not thread-safe
|
|
||||||
is fine since all these applications are single threaded. */
|
|
||||||
|
|
||||||
#if HAVE_CLEARERR_UNLOCKED
|
|
||||||
# undef clearerr
|
|
||||||
# define clearerr(S) clearerr_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FEOF_UNLOCKED
|
|
||||||
# undef feof
|
|
||||||
# define feof(S) feof_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FERROR_UNLOCKED
|
|
||||||
# undef ferror
|
|
||||||
# define ferror(S) ferror_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FFLUSH_UNLOCKED
|
|
||||||
# undef fflush
|
|
||||||
# define fflush(S) fflush_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FPUTC_UNLOCKED
|
|
||||||
# undef fputc
|
|
||||||
# define fputc(C, S) fputc_unlocked (C, S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FREAD_UNLOCKED
|
|
||||||
# undef fread
|
|
||||||
# define fread(P, Z, N, S) fread_unlocked (P, Z, N, S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FWRITE_UNLOCKED
|
|
||||||
# undef fwrite
|
|
||||||
# define fwrite(P, Z, N, S) fwrite_unlocked (P, Z, N, S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_GETC_UNLOCKED
|
|
||||||
# undef getc
|
|
||||||
# define getc(S) getc_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_GETCHAR_UNLOCKED
|
|
||||||
# undef getchar
|
|
||||||
# define getchar(S) getchar_unlocked (S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_PUTC_UNLOCKED
|
|
||||||
# undef putc
|
|
||||||
# define putc(C, S) putc_unlocked (C, S)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_PUTCHAR_UNLOCKED
|
|
||||||
# undef putchar
|
|
||||||
# define putchar(C) putchar_unlocked (C)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
|
|
||||||
((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
|
|
||||||
&& (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
|
|
||||||
|
|
||||||
#define DOT_OR_DOTDOT(Basename) \
|
|
||||||
(Basename[0] == '.' && (Basename[1] == '\0' \
|
|
||||||
|| (Basename[1] == '.' && Basename[2] == '\0')))
|
|
||||||
|
|
||||||
#if SETVBUF_REVERSED
|
|
||||||
# define SETVBUF(Stream, Buffer, Type, Size) \
|
|
||||||
setvbuf (Stream, Type, Buffer, Size)
|
|
||||||
#else
|
|
||||||
# define SETVBUF(Stream, Buffer, Type, Size) \
|
|
||||||
setvbuf (Stream, Buffer, Type, Size)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *base_name PARAMS ((char const *));
|
|
||||||
|
|
||||||
/* Factor out some of the common --help and --version processing code. */
|
|
||||||
|
|
||||||
/* These enum values cannot possibly conflict with the option values
|
|
||||||
ordinarily used by commands, including CHAR_MAX + 1, etc. Avoid
|
|
||||||
CHAR_MIN - 1, as it may equal -1, the getopt end-of-options value. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
GETOPT_HELP_CHAR = (CHAR_MIN - 2),
|
|
||||||
GETOPT_VERSION_CHAR = (CHAR_MIN - 3)
|
|
||||||
};
|
|
||||||
|
|
||||||
#define GETOPT_HELP_OPTION_DECL \
|
|
||||||
"help", no_argument, 0, GETOPT_HELP_CHAR
|
|
||||||
#define GETOPT_VERSION_OPTION_DECL \
|
|
||||||
"version", no_argument, 0, GETOPT_VERSION_CHAR
|
|
||||||
|
|
||||||
#define case_GETOPT_HELP_CHAR \
|
|
||||||
case GETOPT_HELP_CHAR: \
|
|
||||||
usage (EXIT_SUCCESS); \
|
|
||||||
break;
|
|
||||||
|
|
||||||
#include "closeout.h"
|
|
||||||
#include "version-etc.h"
|
|
||||||
|
|
||||||
#define case_GETOPT_VERSION_CHAR(Program_name, Authors) \
|
|
||||||
case GETOPT_VERSION_CHAR: \
|
|
||||||
version_etc (stdout, Program_name, PACKAGE, VERSION, Authors); \
|
|
||||||
exit (EXIT_SUCCESS); \
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifndef MAX
|
|
||||||
# define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MIN
|
|
||||||
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CHAR_BIT
|
|
||||||
# define CHAR_BIT 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The extra casts work around common compiler bugs. */
|
|
||||||
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
|
|
||||||
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
|
|
||||||
It is necessary at least when t == time_t. */
|
|
||||||
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
|
|
||||||
? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
|
|
||||||
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
|
|
||||||
|
|
||||||
/* Upper bound on the string length of an integer converted to string.
|
|
||||||
302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit;
|
|
||||||
add 1 for integer division truncation; add 1 more for a minus sign. */
|
|
||||||
#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)
|
|
||||||
|
|
||||||
#ifndef CHAR_MIN
|
|
||||||
# define CHAR_MIN TYPE_MINIMUM (char)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CHAR_MAX
|
|
||||||
# define CHAR_MAX TYPE_MAXIMUM (char)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCHAR_MIN
|
|
||||||
# define SCHAR_MIN (-1 - SCHAR_MAX)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCHAR_MAX
|
|
||||||
# define SCHAR_MAX (CHAR_MAX == UCHAR_MAX ? CHAR_MAX / 2 : CHAR_MAX)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UCHAR_MAX
|
|
||||||
# define UCHAR_MAX TYPE_MAXIMUM (unsigned char)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SHRT_MIN
|
|
||||||
# define SHRT_MIN TYPE_MINIMUM (short int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SHRT_MAX
|
|
||||||
# define SHRT_MAX TYPE_MAXIMUM (short int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef INT_MAX
|
|
||||||
# define INT_MAX TYPE_MAXIMUM (int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UINT_MAX
|
|
||||||
# define UINT_MAX TYPE_MAXIMUM (unsigned int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LONG_MAX
|
|
||||||
# define LONG_MAX TYPE_MAXIMUM (long)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ULONG_MAX
|
|
||||||
# define ULONG_MAX TYPE_MAXIMUM (unsigned long)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SIZE_MAX
|
|
||||||
# define SIZE_MAX TYPE_MAXIMUM (size_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UINTMAX_MAX
|
|
||||||
# define UINTMAX_MAX TYPE_MAXIMUM (uintmax_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef OFF_T_MIN
|
|
||||||
# define OFF_T_MIN TYPE_MINIMUM (off_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef OFF_T_MAX
|
|
||||||
# define OFF_T_MAX TYPE_MAXIMUM (off_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UID_T_MAX
|
|
||||||
# define UID_T_MAX TYPE_MAXIMUM (uid_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GID_T_MAX
|
|
||||||
# define GID_T_MAX TYPE_MAXIMUM (gid_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef PID_T_MAX
|
|
||||||
# define PID_T_MAX TYPE_MAXIMUM (pid_t)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CHAR_BIT
|
|
||||||
# define CHAR_BIT 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Use this to suppress gcc's `...may be used before initialized' warnings. */
|
|
||||||
#ifdef lint
|
|
||||||
# define IF_LINT(Code) Code
|
|
||||||
#else
|
|
||||||
# define IF_LINT(Code) /* empty */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __attribute__
|
|
||||||
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
|
|
||||||
# define __attribute__(x)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ATTRIBUTE_NORETURN
|
|
||||||
# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ATTRIBUTE_UNUSED
|
|
||||||
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined strdupa
|
|
||||||
# define ASSIGN_STRDUPA(DEST, S) \
|
|
||||||
do { DEST = strdupa(S); } while (0)
|
|
||||||
#else
|
|
||||||
# define ASSIGN_STRDUPA(DEST, S) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
const char *s_ = (S); \
|
|
||||||
size_t len_ = strlen (s_) + 1; \
|
|
||||||
char *tmp_dest_ = (char *) alloca (len_); \
|
|
||||||
DEST = memcpy (tmp_dest_, (s_), len_); \
|
|
||||||
} \
|
|
||||||
while (0)
|
|
||||||
#endif
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
/* system-dependent definitions for fileutils, textutils, and sh-utils packages.
|
|
||||||
Copyright (C) 1989, 1991-2000 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Include sys/types.h before this file. */
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#if !defined(HAVE_MKFIFO)
|
|
||||||
# define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_SYS_PARAM_H
|
|
||||||
# include <sys/param.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* <unistd.h> should be included before any preprocessor test
|
|
||||||
of _POSIX_VERSION. */
|
|
||||||
#if HAVE_UNISTD_H
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STDIN_FILENO
|
|
||||||
# define STDIN_FILENO 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STDOUT_FILENO
|
|
||||||
# define STDOUT_FILENO 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STDERR_FILENO
|
|
||||||
# define STDERR_FILENO 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_LIMITS_H
|
|
||||||
/* limits.h must come before pathmax.h because limits.h on some systems
|
|
||||||
undefs PATH_MAX, whereas pathmax.h sets PATH_MAX. */
|
|
||||||
# include <limits.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "pathmax.h"
|
|
||||||
|
|
||||||
#if TIME_WITH_SYS_TIME
|
|
||||||
# include <sys/time.h>
|
|
||||||
# include <time.h>
|
|
||||||
#else
|
|
||||||
# if HAVE_SYS_TIME_H
|
|
||||||
# include <sys/time.h>
|
|
||||||
# else
|
|
||||||
# include <time.h>
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Since major is a function on SVR4, we can't use `ifndef major'. */
|
|
||||||
#if MAJOR_IN_MKDEV
|
|
||||||
# include <sys/mkdev.h>
|
|
||||||
# define HAVE_MAJOR
|
|
||||||
#endif
|
|
||||||
#if MAJOR_IN_SYSMACROS
|
|
||||||
# include <sys/sysmacros.h>
|
|
||||||
# define HAVE_MAJOR
|
|
||||||
#endif
|
|
||||||
#ifdef major /* Might be defined in sys/types.h. */
|
|
||||||
# define HAVE_MAJOR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HAVE_MAJOR
|
|
||||||
# define major(dev) (((dev) >> 8) & 0xff)
|
|
||||||
# define minor(dev) ((dev) & 0xff)
|
|
||||||
# define makedev(maj, min) (((maj) << 8) | (min))
|
|
||||||
#endif
|
|
||||||
#undef HAVE_MAJOR
|
|
||||||
|
|
||||||
#if HAVE_UTIME_H
|
|
||||||
# include <utime.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Some systems (even some that do have <utime.h>) don't declare this
|
|
||||||
structure anywhere. */
|
|
||||||
#ifndef HAVE_STRUCT_UTIMBUF
|
|
||||||
struct utimbuf
|
|
||||||
{
|
|
||||||
long actime;
|
|
||||||
long modtime;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Don't use bcopy! Use memmove if source and destination may overlap,
|
|
||||||
memcpy otherwise. */
|
|
||||||
|
|
||||||
#if HAVE_STRING_H
|
|
||||||
# if !STDC_HEADERS && HAVE_MEMORY_H
|
|
||||||
# include <memory.h>
|
|
||||||
# endif
|
|
||||||
# include <string.h>
|
|
||||||
#else
|
|
||||||
# include <strings.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#ifndef errno
|
|
||||||
extern int errno;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_STDLIB_H
|
|
||||||
# define getopt system_getopt
|
|
||||||
# include <stdlib.h>
|
|
||||||
# undef getopt
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The following test is to work around the gross typo in
|
|
||||||
systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE
|
|
||||||
is defined to 0, not 1. */
|
|
||||||
#if !EXIT_FAILURE
|
|
||||||
# undef EXIT_FAILURE
|
|
||||||
# define EXIT_FAILURE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef EXIT_SUCCESS
|
|
||||||
# define EXIT_SUCCESS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_FCNTL_H
|
|
||||||
# include <fcntl.h>
|
|
||||||
#else
|
|
||||||
# include <sys/file.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined (SEEK_SET)
|
|
||||||
# define SEEK_SET 0
|
|
||||||
# define SEEK_CUR 1
|
|
||||||
# define SEEK_END 2
|
|
||||||
#endif
|
|
||||||
#ifndef F_OK
|
|
||||||
# define F_OK 0
|
|
||||||
# define X_OK 1
|
|
||||||
# define W_OK 2
|
|
||||||
# define R_OK 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* For systems that distinguish between text and binary I/O.
|
|
||||||
O_BINARY is usually declared in fcntl.h */
|
|
||||||
#if !defined O_BINARY && defined _O_BINARY
|
|
||||||
/* For MSC-compatible compilers. */
|
|
||||||
# define O_BINARY _O_BINARY
|
|
||||||
# define O_TEXT _O_TEXT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __BEOS__
|
|
||||||
/* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */
|
|
||||||
# undef O_BINARY
|
|
||||||
# undef O_TEXT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if O_BINARY
|
|
||||||
# ifndef __DJGPP__
|
|
||||||
# define setmode _setmode
|
|
||||||
# define fileno(_fp) _fileno (_fp)
|
|
||||||
# endif /* not DJGPP */
|
|
||||||
# define SET_BINARY(_f) do {if (!isatty(_f)) setmode (_f, O_BINARY);} while (0)
|
|
||||||
# define SET_BINARY2(_f1, _f2) \
|
|
||||||
do { \
|
|
||||||
if (!isatty (_f1)) \
|
|
||||||
{ \
|
|
||||||
setmode (_f1, O_BINARY); \
|
|
||||||
if (!isatty (_f2)) \
|
|
||||||
setmode (_f2, O_BINARY); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
#else
|
|
||||||
# define SET_BINARY(f) (void)0
|
|
||||||
# define SET_BINARY2(f1,f2) (void)0
|
|
||||||
# define O_BINARY 0
|
|
||||||
# define O_TEXT 0
|
|
||||||
#endif /* O_BINARY */
|
|
||||||
|
|
||||||
#if HAVE_DIRENT_H
|
|
||||||
# include <dirent.h>
|
|
||||||
# define NLENGTH(direct) (strlen((direct)->d_name))
|
|
||||||
#else /* not HAVE_DIRENT_H */
|
|
||||||
# define dirent direct
|
|
||||||
# define NLENGTH(direct) ((direct)->d_namlen)
|
|
||||||
# if HAVE_SYS_NDIR_H
|
|
||||||
# include <sys/ndir.h>
|
|
||||||
# endif /* HAVE_SYS_NDIR_H */
|
|
||||||
# if HAVE_SYS_DIR_H
|
|
||||||
# include <sys/dir.h>
|
|
||||||
# endif /* HAVE_SYS_DIR_H */
|
|
||||||
# if HAVE_NDIR_H
|
|
||||||
# include <ndir.h>
|
|
||||||
# endif /* HAVE_NDIR_H */
|
|
||||||
#endif /* HAVE_DIRENT_H */
|
|
||||||
|
|
||||||
#if CLOSEDIR_VOID
|
|
||||||
/* Fake a return value. */
|
|
||||||
# define CLOSEDIR(d) (closedir (d), 0)
|
|
||||||
#else
|
|
||||||
# define CLOSEDIR(d) closedir (d)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Get or fake the disk device blocksize.
|
|
||||||
Usually defined by sys/param.h (if at all). */
|
|
||||||
#if !defined DEV_BSIZE && defined BSIZE
|
|
||||||
# define DEV_BSIZE BSIZE
|
|
||||||
#endif
|
|
||||||
#if !defined DEV_BSIZE && defined BBSIZE /* SGI */
|
|
||||||
# define DEV_BSIZE BBSIZE
|
|
||||||
#endif
|
|
||||||
#ifndef DEV_BSIZE
|
|
||||||
# define DEV_BSIZE 4096
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Extract or fake data from a `struct stat'.
|
|
||||||
ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
|
|
||||||
ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
|
|
||||||
ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
|
|
||||||
#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
|
|
||||||
# define ST_BLKSIZE(statbuf) DEV_BSIZE
|
|
||||||
# if defined(_POSIX_SOURCE) || !defined(BSIZE) /* fileblocks.c uses BSIZE. */
|
|
||||||
# define ST_NBLOCKS(statbuf) \
|
|
||||||
(S_ISREG ((statbuf).st_mode) \
|
|
||||||
|| S_ISDIR ((statbuf).st_mode) \
|
|
||||||
? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0)
|
|
||||||
# else /* !_POSIX_SOURCE && BSIZE */
|
|
||||||
# define ST_NBLOCKS(statbuf) \
|
|
||||||
(S_ISREG ((statbuf).st_mode) \
|
|
||||||
|| S_ISDIR ((statbuf).st_mode) \
|
|
||||||
? st_blocks ((statbuf).st_size) : 0)
|
|
||||||
# endif /* !_POSIX_SOURCE && BSIZE */
|
|
||||||
#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
|
|
||||||
/* Some systems, like Sequents, return st_blksize of 0 on pipes. */
|
|
||||||
# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \
|
|
||||||
? (statbuf).st_blksize : DEV_BSIZE)
|
|
||||||
# if defined(hpux) || defined(__hpux__) || defined(__hpux)
|
|
||||||
/* HP-UX counts st_blocks in 1024-byte units.
|
|
||||||
This loses when mixing HP-UX and BSD filesystems with NFS. */
|
|
||||||
# define ST_NBLOCKSIZE 1024
|
|
||||||
# else /* !hpux */
|
|
||||||
# if defined(_AIX) && defined(_I386)
|
|
||||||
/* AIX PS/2 counts st_blocks in 4K units. */
|
|
||||||
# define ST_NBLOCKSIZE (4 * 1024)
|
|
||||||
# else /* not AIX PS/2 */
|
|
||||||
# if defined(_CRAY)
|
|
||||||
# define ST_NBLOCKS(statbuf) \
|
|
||||||
(S_ISREG ((statbuf).st_mode) \
|
|
||||||
|| S_ISDIR ((statbuf).st_mode) \
|
|
||||||
? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
|
|
||||||
# endif /* _CRAY */
|
|
||||||
# endif /* not AIX PS/2 */
|
|
||||||
# endif /* !hpux */
|
|
||||||
#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
|
|
||||||
|
|
||||||
#ifndef ST_NBLOCKS
|
|
||||||
# define ST_NBLOCKS(statbuf) \
|
|
||||||
(S_ISREG ((statbuf).st_mode) \
|
|
||||||
|| S_ISDIR ((statbuf).st_mode) \
|
|
||||||
? (statbuf).st_blocks : 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ST_NBLOCKSIZE
|
|
||||||
# define ST_NBLOCKSIZE 512
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sys2.h"
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
/* touch -- change modification and access times of files
|
|
||||||
Copyright (C) 87, 1989-1991, 1995-2001 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
/* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
|
|
||||||
and Randy Smith. */
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "system.h"
|
|
||||||
#include "argmatch.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include "getdate.h"
|
|
||||||
#include "posixtm.h"
|
|
||||||
#include "quote.h"
|
|
||||||
#include "safe-read.h"
|
|
||||||
|
|
||||||
/* The official name of this program (e.g., no `g' prefix). */
|
|
||||||
#define PROGRAM_NAME "touch"
|
|
||||||
|
|
||||||
#define AUTHORS \
|
|
||||||
"Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie, and Randy Smith"
|
|
||||||
|
|
||||||
#ifndef STDC_HEADERS
|
|
||||||
time_t time ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Bitmasks for `change_times'. */
|
|
||||||
#define CH_ATIME 1
|
|
||||||
#define CH_MTIME 2
|
|
||||||
|
|
||||||
#if !defined O_NDELAY
|
|
||||||
# define O_NDELAY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined O_NONBLOCK
|
|
||||||
# define O_NONBLOCK O_NDELAY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined O_NOCTTY
|
|
||||||
# define O_NOCTTY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The name by which this program was run. */
|
|
||||||
char *program_name;
|
|
||||||
|
|
||||||
/* Which timestamps to change. */
|
|
||||||
static int change_times;
|
|
||||||
|
|
||||||
/* (-c) If nonzero, don't create if not already there. */
|
|
||||||
static int no_create;
|
|
||||||
|
|
||||||
/* (-d) If nonzero, date supplied on command line in get_date formats. */
|
|
||||||
static int flexible_date;
|
|
||||||
|
|
||||||
/* (-r) If nonzero, use times from a reference file. */
|
|
||||||
static int use_ref;
|
|
||||||
|
|
||||||
/* (-t) If nonzero, date supplied on command line in POSIX format. */
|
|
||||||
static int posix_date;
|
|
||||||
|
|
||||||
/* If nonzero, the only thing we have to do is change both the
|
|
||||||
modification and access time to the current time, so we don't
|
|
||||||
have to own the file, just be able to read and write it.
|
|
||||||
On some systems, we can do this if we own the file, even though
|
|
||||||
we have neither read nor write access to it. */
|
|
||||||
static int amtime_now;
|
|
||||||
|
|
||||||
/* New time to use when setting time. */
|
|
||||||
static time_t newtime;
|
|
||||||
|
|
||||||
/* File to use for -r. */
|
|
||||||
static char *ref_file;
|
|
||||||
|
|
||||||
/* Info about the reference file. */
|
|
||||||
static struct stat ref_stats;
|
|
||||||
|
|
||||||
/* For long options that have no equivalent short option, use a
|
|
||||||
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
TIME_OPTION = CHAR_MAX + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct option const longopts[] =
|
|
||||||
{
|
|
||||||
{"time", required_argument, 0, TIME_OPTION},
|
|
||||||
{"no-create", no_argument, 0, 'c'},
|
|
||||||
{"date", required_argument, 0, 'd'},
|
|
||||||
{"file", required_argument, 0, 'r'}, /* FIXME: phase out --file */
|
|
||||||
{"reference", required_argument, 0, 'r'},
|
|
||||||
{GETOPT_HELP_OPTION_DECL},
|
|
||||||
{GETOPT_VERSION_OPTION_DECL},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Valid arguments to the `--time' option. */
|
|
||||||
static char const* const time_args[] =
|
|
||||||
{
|
|
||||||
"atime", "access", "use", "mtime", "modify", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The bits in `change_times' that those arguments set. */
|
|
||||||
static int const time_masks[] =
|
|
||||||
{
|
|
||||||
CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Update the time of file FILE according to the options given.
|
|
||||||
Return 0 if successful, 1 if an error occurs. */
|
|
||||||
|
|
||||||
static int
|
|
||||||
touch (const char *file)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct stat sbuf;
|
|
||||||
int fd = -1;
|
|
||||||
int open_errno = 0;
|
|
||||||
|
|
||||||
if (! no_create)
|
|
||||||
{
|
|
||||||
/* Try to open FILE, creating it if necessary. */
|
|
||||||
fd = open (file, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
|
|
||||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
||||||
if (fd == -1)
|
|
||||||
open_errno = errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! amtime_now)
|
|
||||||
{
|
|
||||||
/* We're setting only one of the time values. stat the target to get
|
|
||||||
the other one. If we have the file descriptor already, use fstat.
|
|
||||||
Otherwise, either we're in no-create mode (and hence didn't call open)
|
|
||||||
or FILE is inaccessible or a directory, so we have to use stat. */
|
|
||||||
if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
|
|
||||||
{
|
|
||||||
if (open_errno)
|
|
||||||
error (0, open_errno, _("creating %s"), quote (file));
|
|
||||||
else
|
|
||||||
error (0, errno, _("getting attributes of %s"), quote (file));
|
|
||||||
close (fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd != -1 && close (fd) < 0)
|
|
||||||
{
|
|
||||||
error (0, errno, _("creating %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amtime_now)
|
|
||||||
{
|
|
||||||
/* Pass NULL to utime so it will not fail if we just have
|
|
||||||
write access to the file, but don't own it. */
|
|
||||||
status = utime (file, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct utimbuf utb;
|
|
||||||
|
|
||||||
/* There's currently no interface to set file timestamps with
|
|
||||||
better than 1-second resolution, so discard any fractional
|
|
||||||
part of the source timestamp. */
|
|
||||||
|
|
||||||
if (use_ref)
|
|
||||||
{
|
|
||||||
utb.actime = ref_stats.st_atime;
|
|
||||||
utb.modtime = ref_stats.st_mtime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
utb.actime = utb.modtime = newtime;
|
|
||||||
|
|
||||||
if (!(change_times & CH_ATIME))
|
|
||||||
utb.actime = sbuf.st_atime;
|
|
||||||
|
|
||||||
if (!(change_times & CH_MTIME))
|
|
||||||
utb.modtime = sbuf.st_mtime;
|
|
||||||
|
|
||||||
status = utime (file, &utb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status)
|
|
||||||
{
|
|
||||||
if (open_errno)
|
|
||||||
error (0, open_errno, _("creating %s"), quote (file));
|
|
||||||
else
|
|
||||||
error (0, errno, _("setting times of %s"), quote (file));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
usage (int status)
|
|
||||||
{
|
|
||||||
if (status != 0)
|
|
||||||
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
||||||
program_name);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
|
|
||||||
printf (_(" or: %s [-acm] MMDDhhmm[YY] FILE... (obsolescent)\n"),
|
|
||||||
program_name);
|
|
||||||
printf (_("\
|
|
||||||
Update the access and modification times of each FILE to the current time.\n\
|
|
||||||
\n\
|
|
||||||
-a change only the access time\n\
|
|
||||||
-c, --no-create do not create any files\n\
|
|
||||||
-d, --date=STRING parse STRING and use it instead of current time\n\
|
|
||||||
-f (ignored)\n\
|
|
||||||
-m change only the modification time\n\
|
|
||||||
-r, --reference=FILE use this file's times instead of current time\n\
|
|
||||||
-t STAMP use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
|
|
||||||
--time=WORD set time given by WORD: access atime use (same as -a)\n\
|
|
||||||
modify mtime (same as -m)\n\
|
|
||||||
--help display this help and exit\n\
|
|
||||||
--version output version information and exit\n\
|
|
||||||
\n\
|
|
||||||
Note that the three time-date formats recognized for the -d and -t options\n\
|
|
||||||
and for the obsolescent argument are all different.\n\
|
|
||||||
"));
|
|
||||||
puts (_("\nReport bugs to <[email protected]>."));
|
|
||||||
}
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
int date_set = 0;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
program_name = argv[0];
|
|
||||||
setlocale (LC_ALL, "");
|
|
||||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
||||||
textdomain (PACKAGE);
|
|
||||||
|
|
||||||
atexit (close_stdout);
|
|
||||||
|
|
||||||
change_times = no_create = use_ref = posix_date = flexible_date = 0;
|
|
||||||
newtime = (time_t) -1;
|
|
||||||
|
|
||||||
while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
change_times |= CH_ATIME;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
no_create++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
|
||||||
flexible_date++;
|
|
||||||
newtime = get_date (optarg, NULL);
|
|
||||||
if (newtime == (time_t) -1)
|
|
||||||
error (1, 0, _("invalid date format %s"), quote (optarg));
|
|
||||||
date_set++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'm':
|
|
||||||
change_times |= CH_MTIME;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r':
|
|
||||||
use_ref++;
|
|
||||||
ref_file = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 't':
|
|
||||||
posix_date++;
|
|
||||||
newtime = posixtime (optarg,
|
|
||||||
PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS);
|
|
||||||
if (newtime == (time_t) -1)
|
|
||||||
error (1, 0, _("invalid date format %s"), quote (optarg));
|
|
||||||
date_set++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TIME_OPTION: /* --time */
|
|
||||||
change_times |= XARGMATCH ("--time", optarg,
|
|
||||||
time_args, time_masks);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case_GETOPT_HELP_CHAR;
|
|
||||||
|
|
||||||
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
||||||
|
|
||||||
default:
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (change_times == 0)
|
|
||||||
change_times = CH_ATIME | CH_MTIME;
|
|
||||||
|
|
||||||
if ((use_ref && (posix_date || flexible_date))
|
|
||||||
|| (posix_date && flexible_date))
|
|
||||||
{
|
|
||||||
error (0, 0, _("cannot specify times from more than one source"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (use_ref)
|
|
||||||
{
|
|
||||||
if (stat (ref_file, &ref_stats))
|
|
||||||
error (1, errno, _("getting attributes of %s"), quote (ref_file));
|
|
||||||
date_set++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The obsolescent `MMDDhhmm[YY]' form is valid IFF there are
|
|
||||||
two or more non-option arguments. */
|
|
||||||
if (!date_set && 2 <= argc - optind && !STREQ (argv[optind - 1], "--"))
|
|
||||||
{
|
|
||||||
newtime = posixtime (argv[optind], PDS_TRAILING_YEAR);
|
|
||||||
if (newtime != (time_t) -1)
|
|
||||||
{
|
|
||||||
optind++;
|
|
||||||
date_set++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!date_set)
|
|
||||||
{
|
|
||||||
if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
|
|
||||||
amtime_now = 1;
|
|
||||||
else
|
|
||||||
time (&newtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind == argc)
|
|
||||||
{
|
|
||||||
error (0, 0, _("file arguments missing"));
|
|
||||||
usage (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; optind < argc; ++optind)
|
|
||||||
err += touch (argv[optind]);
|
|
||||||
|
|
||||||
exit (err != 0);
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
SubDir OBOS_TOP src kernel apps pwd ;
|
|
||||||
|
|
||||||
KernelObjects <$(SOURCE_GRIST)>main.c : -fpic -Wno-unused ;
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 1991, 1993, 1994
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. All advertising materials mentioning features or use of this software
|
|
||||||
* must display the following acknowledgement:
|
|
||||||
* This product includes software developed by the University of
|
|
||||||
* California, Berkeley and its contributors.
|
|
||||||
* 4. Neither the name of the University nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <ktypes.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
static char *getcwd_logical(void);
|
|
||||||
int usage(void);
|
|
||||||
int version(void);
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int physical = false;
|
|
||||||
int argOption;
|
|
||||||
char *pcTemp;
|
|
||||||
int indexptr = 0;
|
|
||||||
char * const * optargv = argv;
|
|
||||||
|
|
||||||
struct option help_opt = { "help", no_argument, 0, 1 } ;
|
|
||||||
struct option version_opt = { "version", no_argument, 0, 2 } ;
|
|
||||||
|
|
||||||
struct option options[] = {
|
|
||||||
help_opt, version_opt, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
while ((argOption = getopt_long(argc, optargv, "LP", options, &indexptr)) != -1) {
|
|
||||||
|
|
||||||
switch (argOption) {
|
|
||||||
case 'L': // Logical
|
|
||||||
physical = false;
|
|
||||||
break;
|
|
||||||
case 'P': // Physical
|
|
||||||
physical = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
switch (options[indexptr].val) {
|
|
||||||
case 1: // help
|
|
||||||
usage();
|
|
||||||
exit(0);
|
|
||||||
case 2: // version
|
|
||||||
version();
|
|
||||||
exit(0);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argc -= optind;
|
|
||||||
|
|
||||||
if (argc != 0) {
|
|
||||||
usage();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/*
|
|
||||||
* If the logical option was specified, and we can not find the logical currrent directory
|
|
||||||
* then behave as if -P (physical) was specified.
|
|
||||||
*/
|
|
||||||
if ((!physical && (pcTemp = getcwd_logical()) != NULL) || (pcTemp = getcwd(NULL, 0)) != NULL)
|
|
||||||
printf("%s\n", pcTemp);
|
|
||||||
else
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
return B_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
usage(void)
|
|
||||||
{
|
|
||||||
(void)fprintf(stderr,
|
|
||||||
"pwd OBOS (C)2003 MIT License. (http://www.openbeos.net/)
|
|
||||||
|
|
||||||
Usage: pwd [OPTION]...
|
|
||||||
Print the current working directory path
|
|
||||||
|
|
||||||
--help display this help and exit
|
|
||||||
--version display version information and exit
|
|
||||||
-L treat path as a Logical device
|
|
||||||
-P treat path as a Physical device (default)\n" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
version(void)
|
|
||||||
{
|
|
||||||
(void)fprintf(stderr, "pwd OBOS (C)2003 MIT License. (http://www.openbeos.net/)\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *
|
|
||||||
getcwd_logical(void)
|
|
||||||
{
|
|
||||||
struct stat logical, physical;
|
|
||||||
char *pwd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that $PWD is an absolute logical pathname referring to the current working directory.
|
|
||||||
*/
|
|
||||||
if ((pwd = getenv("PWD")) != NULL && *pwd == '/') {
|
|
||||||
if (stat(pwd, &logical) == -1 || stat(".", &physical) == -1)
|
|
||||||
return (NULL);
|
|
||||||
if (logical.st_dev == physical.st_dev && logical.st_ino == physical.st_ino)
|
|
||||||
return (pwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = ENOENT;
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user