cdesktopenv/cde/lib/tt/bin/shell/copier.C

476 lines
11 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
//%% (c) Copyright 1993, 1994 Novell, Inc.
//%% $XConsortium: copier.C /main/3 1995/10/20 16:35:57 rswiston $
/*
* copier.cc - Link Service/ToolTalk wrapper for cp(1).
*
* Copyright (c) 1990 by Sun Microsystems, Inc.
*
*/
#include "tt_options.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <errno.h>
#include "api/c/tt_c.h"
#include "util/tt_path.h"
#include "util/tt_gettext.h"
#include "util/copyright.h"
#include "copier.h"
/*
* External variables
*/
extern char *_tt_get_realpath(char *, char *);
/*
* copier::copier()
*/
copier::
copier( char *arg0 )
{
if (arg0 != NULL) {
char *base = strrchr( arg0, '/' );
if (base == NULL) {
base = arg0;
} else {
base++; /* Don't want the '/' */
}
_prog_name = base;
_process_name = _prog_name;
}
_args = new _Tt_string_list();
_from_paths = new _Tt_string_list();
_should_cp = TRUE;
_recurse = FALSE;
_preserve = FALSE;
_clonedir_mode = FALSE;
_tt_opened = FALSE;
_to_path_is_dir = 0;
}
copier::
~copier()
{
}
/*
* copier::do_cp() - Use system() to invoke cp(1), and return its exit status.
* We can just use _args, since we never get here when our one
* cp-incompatible option (-L) has been given.
*/
int copier::
do_cp()
{
_Tt_string cmd( "cp" );
_Tt_string_list_cursor arg_cursor( _args );
while (arg_cursor.next()) {
cmd = cmd.cat( " " ).cat( *arg_cursor );
}
//printf( "Invoking: %s\n", (char *)cmd );
int sys_status = system( (char *)cmd );
if (WIFEXITED(sys_status)) {
return WEXITSTATUS(sys_status);
} else {
fprintf( stderr,
"%s: system(\"%s\"): %d\n",
(char *)_process_name, (char *)cmd, sys_status );
return 1;
}
}
/*
* copier::do_ttcp() - Use tttar(1) to copy the objects of the _from_paths.
*
* Algorithm:
*
* if (_clonedir_mode) {
* cd _from_path; tttar cfhL - . | (cd ../_to_path; tttar xfLp? -)
* } else {
* if (_to_path_is_dir) {
* tttar cfhL - _from_paths | (cd _to_path ; tttar xfLp? - )
* } else {
* tttar cfhL - _from_path | tttar xfLp? - -rename from to
* }
* }
*/
Tt_status copier::
do_ttcp()
{
_Tt_string cmd;
_Tt_string_list_cursor from_path_cursor( _from_paths );
Tt_status err;
if (_clonedir_mode) {
#ifdef DO_TTTAR_AFTER_CP
if (mkdir( (char *)_to_path, S_IRWXU ) != 0) {
return TT_ERR_PATH;
}
#endif
cmd = cmd.cat( "cd " ).cat( _from_paths->top()).cat( " ; ");
}
cmd = cmd.cat( "tttar cfhL -" );
while (from_path_cursor.next()) {
_Tt_string from_path = *from_path_cursor;
if (! this->can_cp( from_path )) {
/*
* Don't tttar any paths we know that cp(1) will
* reject. We do this for clonedir mode, too, so
* we can return if the cloning shouldn't be done.
*/
from_path_cursor.remove();
} else {
/*
* tt_file_destroy() any path that cp(1) will delete
*/
_Tt_string path2zap = _to_path;
if (_to_path_is_dir) {
/*
* cp(1) will overwrite any entry in _to_path
* that has the same name as a _from_path.
*/
_Tt_string dir, base;
base = from_path.rsplit( '/', dir );
path2zap = _to_path.cat( "/" ).cat( base );
}
err = tt_file_destroy( (char *)path2zap );
if (err > TT_WRN_LAST) {
fprintf( stderr,
catgets(_ttcatd, 8, 12,
"%s: Could not remove "
"ToolTalk objects of %s "
"because %s\n"),
(char *)_process_name,
(char *)path2zap,
tt_status_message(err) );
}
}
}
if (_from_paths->count() <= 0) {
return TT_OK;
}
if (_clonedir_mode) {
/*
* In clonedir mode, we just tttar up everything in
* the directory we're cloning.
*/
cmd = cmd.cat( " ." );
} else {
from_path_cursor.reset();
while (from_path_cursor.next()) {
cmd = cmd.cat( " " ).cat( *from_path_cursor );
}
}
cmd = cmd.cat( " |" );
if (_to_path_is_dir) {
cmd = cmd.cat( " ( cd " );
if (_clonedir_mode) {
char realpath_buf[ MAXPATHLEN ];
/*
* If we're in _clonedir_mode, then we'll be
* cd'ing down into _from_path, and so we want
* a realpath of _to_path to cd over to,
* because if _from_path is a symlink then
* in _from_path "../_to_path" is _not_ _to_path.
*/
char *real_to_path = _tt_get_realpath( (char *)_to_path,
realpath_buf );
if (real_to_path == NULL) {
fprintf( stderr, "%s: %s: %s\n",
(char *)_process_name,
(char *)_to_path, strerror(errno) );
return TT_ERR_PATH;
}
cmd = cmd.cat( real_to_path );
} else {
cmd = cmd.cat( _to_path );
}
cmd = cmd.cat( " ;" );
}
cmd = cmd.cat( " tttar xfL" );
if (_preserve) {
cmd = cmd.cat( "p" );
}
cmd = cmd.cat( " -" );
/*
* Use the hack we built into tttar(1) to rename paths
* as they're extracted. Rename each _from_path to _to_path.
*/
from_path_cursor.reset();
while (from_path_cursor.next()) {
_Tt_string from_path = *from_path_cursor;
cmd = cmd.cat( " -rename " ).cat( from_path )
.cat( " " ).cat( _to_path );
/*
* If the copy is to be made in a subdirectory of _to_path,
* make tttar maps _from_path to the appropriate
* subdirectory of _to_path as it extracts.
*/
if ((_to_path_is_dir) && (! _clonedir_mode)) {
_Tt_string dir_name;
cmd = cmd.cat( "/" )
.cat( from_path.rsplit( '/', dir_name ));
}
}
if (_to_path_is_dir) {
cmd = cmd.cat( " )" );
}
//printf( "Invoking: %s\n", (char *)cmd );
int sys_status = system( (char *)cmd );
#ifdef DO_TTTAR_AFTER_CP
if (_clonedir_mode) {
/*
* remove the target directory, so that cp(1) won't
* see it and make _from_path a subdirectory of it.
*/
if (rmdir( (char *)_to_path ) != 0) {
fprintf( stderr, "%s: rmdir(\"%s\"): %s\n",
(char *)_process_name, (char *)_to_path,
strerror(errno) );
return TT_ERR_PATH;
}
}
#endif
if (WIFEXITED(sys_status)) {
if (WEXITSTATUS(sys_status) == 0) {
return TT_OK;
} else {
return TT_ERR_INTERNAL;
}
} else {
fprintf( stderr,
"%s: system(\"%s\"): %d\n",
(char *)_process_name, (char *)cmd, sys_status );
return TT_ERR_INTERNAL;
}
} /* do_ttcp() */
/*
* copier::can_cp() - Can we copy this path to _to_path?
*
* TO_DO: can_cp() can be as tricky as you like.
*/
bool_t copier::
can_cp( _Tt_string from_path )
{
struct stat stat_buf;
if (stat( (char *)from_path, &stat_buf) != 0) {
return FALSE;
}
if (S_ISDIR(stat_buf.st_mode) && (! _recurse)) {
return FALSE;
}
return TRUE;
}
/*
* copier::open_tt()
*/
Tt_status copier::
open_tt()
{
char *process_id = tt_open();
Tt_status err = tt_ptr_error( process_id );
if (err == TT_OK) {
_process_id = process_id;
_tt_opened = TRUE;
} else if (err > TT_WRN_LAST) {
fprintf( stderr,
"%s: tt_open(): %s\n",
(char *)_process_name, tt_status_message( err ) );
}
return err;
}
/*
* copier::close_tt()
*/
Tt_status copier::
close_tt()
{
if (! _tt_opened) {
return TT_OK;
}
Tt_status err = tt_close();
if (err > TT_WRN_LAST) {
fprintf( stderr,
"%s: tt_close(): %s\n",
(char *)_process_name, tt_status_message( err ) );
}
return err;
}
/*
* copier::parse_args()
*/
void copier::
parse_args( int argc, char **argv )
{
for ( int arg_num = 1; arg_num < argc; arg_num++ ) {
_Tt_string arg( argv[arg_num] );
_args->append( arg );
if (arg[0] == '-') {
this->_parse_arg( (char *)arg );
} else {
if (arg_num == argc - 1) {
_to_path = arg;
} else {
_from_paths->append( arg );
}
}
}
if ((_to_path.len() <= 0) || (_from_paths->count() <= 0)) {
this->usage();
exit(1);
}
if (_from_paths->count() > 1) {
/*
* If multiple things to move, the place we're moving them to
* must be a directory.
*/
struct stat stat_buf;
if (stat( (char *)_to_path, &stat_buf) != 0) {
fprintf( stderr, "%s: %s: ", (char *)_process_name,
(char *)_to_path );
perror(NULL);
exit(2);
}
if (! S_ISDIR(stat_buf.st_mode)) {
fprintf( stderr, "%s: %s: %s\n",
(char *)_process_name, (char *)_to_path,
strerror(ENOTDIR) );
this->usage();
exit(2);
}
_to_path_is_dir = TRUE;
} else {
struct stat stat_buf;
_to_path_is_dir = FALSE;
if (stat( (char *)_to_path, &stat_buf) == 0) {
_to_path_is_dir = S_ISDIR(stat_buf.st_mode);
} else {
/*
* If you "cp -r dir1 dir2" and dir2 doesn't
* exist, cp(1) creates it and copies dir1's
* contents into it. What we need to do in this
* case is to
* 1. mkdir( dir2 ), [so we can cd there]
* 2. (cd dir1; tttar cf - .) | (cd dir2; tttar xf -)
* 3. rmdir( dir2 ) [so cp(1) will do right thing]
*/
_Tt_string from_path = _from_paths->top();
if ( (_from_paths->count() == 1)
&& (stat( (char *)from_path, &stat_buf) == 0)
&& S_ISDIR(stat_buf.st_mode))
{
_clonedir_mode = TRUE;
_to_path_is_dir = TRUE;
}
}
}
} /* parse_args() */
/*
* copier::_parse_arg() - Parse an option
*
* If you add any options not supported by cp(1) and still expect
* it to be system()'d, fix ::do_cp() to pass cp(1) the right args.
*/
void copier::
_parse_arg( char *arg )
{
if (arg == NULL) {
return;
}
int n = -1;
while (arg[++n] != '\0') {
switch (arg[n]) {
case '-':
if (n != 0) {
this->usage();
exit(1);
}
break;
case 'i':
this->usage();
exit(1);
break;
case 'L':
_should_cp = FALSE;
break;
case 'p':
_preserve = TRUE;
break;
case 'r':
case 'R':
_recurse = TRUE;
break;
case 'v':
_TT_PRINT_VERSIONS((char *)_prog_name)
exit(0);
break;
case 'h':
default:
this->usage();
exit(1);
}
}
} /* _parse_arg() */
/*
* copier::usage()
*/
void copier::
usage(FILE *fs) const
{
fprintf( fs,
catgets(_ttcatd, 8, 13,
"Usage: %s [-pL] file1 file2\n"
" %s [-prRL] path1 [path2 ...] dir\n"
" %s -v\n"
" %s -h\n"),
(char *)_prog_name, (char *)_prog_name, (char *)_prog_name,
(char *)_prog_name );
fprintf( fs, "%s",
catgets(_ttcatd, 8, 14,
"\t-L do not perform a cp(1)\n"
"\t-v print the version number and quit\n"
"\t-h print this message\n" ));
}