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 flex ;
|
||||
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 keymap ;
|
||||
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 patch ;
|
||||
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 sharutils ;
|
||||
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 lm@sgi.com.
|
||||
--si and large file support added by eggert@twinsun.com. */
|
||||
|
||||
#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 tege@sics.se, Torbjorn Granlund,
|
||||
and djm@ai.mit.edu, David MacKenzie.
|
||||
Variable blocks added by lm@sgi.com and eggert@twinsun.com.
|
||||
*/
|
||||
|
||||
#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 <djm@ai.mit.edu> */
|
||||
|
||||
#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 <djm@ai.mit.edu> */
|
||||
|
||||
#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