/* * 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 #include #include #include #include #include #include #include #include "Tt/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" )); }