2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 //%% (c) Copyright 1993, 1994 Hewlett-Packard Company
24 //%% (c) Copyright 1993, 1994 International Business Machines Corp.
25 //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
26 //%% (c) Copyright 1993, 1994 Novell, Inc.
27 //%% $XConsortium: copier.C /main/3 1995/10/20 16:35:57 rswiston $
29 * copier.cc - Link Service/ToolTalk wrapper for cp(1).
31 * Copyright (c) 1990 by Sun Microsystems, Inc.
35 #include "tt_options.h"
42 #include <sys/param.h>
44 #include "api/c/tt_c.h"
45 #include "util/tt_path.h"
46 #include "util/tt_gettext.h"
47 #include "util/copyright.h"
53 extern char *_tt_get_realpath(char *, char *);
62 char *base = strrchr( arg0, '/' );
66 base++; /* Don't want the '/' */
69 _process_name = _prog_name;
71 _args = new _Tt_string_list();
72 _from_paths = new _Tt_string_list();
76 _clonedir_mode = FALSE;
86 * copier::do_cp() - Use system() to invoke cp(1), and return its exit status.
87 * We can just use _args, since we never get here when our one
88 * cp-incompatible option (-L) has been given.
93 _Tt_string cmd( "cp" );
94 _Tt_string_list_cursor arg_cursor( _args );
96 while (arg_cursor.next()) {
97 cmd = cmd.cat( " " ).cat( *arg_cursor );
99 //printf( "Invoking: %s\n", (char *)cmd );
100 int sys_status = system( (char *)cmd );
101 if (WIFEXITED(sys_status)) {
102 return WEXITSTATUS(sys_status);
105 "%s: system(\"%s\"): %d\n",
106 (char *)_process_name, (char *)cmd, sys_status );
112 * copier::do_ttcp() - Use tttar(1) to copy the objects of the _from_paths.
116 * if (_clonedir_mode) {
117 * cd _from_path; tttar cfhL - . | (cd ../_to_path; tttar xfLp? -)
119 * if (_to_path_is_dir) {
120 * tttar cfhL - _from_paths | (cd _to_path ; tttar xfLp? - )
122 * tttar cfhL - _from_path | tttar xfLp? - -rename from to
130 _Tt_string_list_cursor from_path_cursor( _from_paths );
133 if (_clonedir_mode) {
134 #ifdef DO_TTTAR_AFTER_CP
135 if (mkdir( (char *)_to_path, S_IRWXU ) != 0) {
139 cmd = cmd.cat( "cd " ).cat( _from_paths->top()).cat( " ; ");
141 cmd = cmd.cat( "tttar cfhL -" );
142 while (from_path_cursor.next()) {
143 _Tt_string from_path = *from_path_cursor;
144 if (! this->can_cp( from_path )) {
146 * Don't tttar any paths we know that cp(1) will
147 * reject. We do this for clonedir mode, too, so
148 * we can return if the cloning shouldn't be done.
150 from_path_cursor.remove();
153 * tt_file_destroy() any path that cp(1) will delete
155 _Tt_string path2zap = _to_path;
156 if (_to_path_is_dir) {
158 * cp(1) will overwrite any entry in _to_path
159 * that has the same name as a _from_path.
161 _Tt_string dir, base;
162 base = from_path.rsplit( '/', dir );
163 path2zap = _to_path.cat( "/" ).cat( base );
165 err = tt_file_destroy( (char *)path2zap );
166 if (err > TT_WRN_LAST) {
168 catgets(_ttcatd, 8, 12,
169 "%s: Could not remove "
170 "ToolTalk objects of %s "
172 (char *)_process_name,
174 tt_status_message(err) );
178 if (_from_paths->count() <= 0) {
181 if (_clonedir_mode) {
183 * In clonedir mode, we just tttar up everything in
184 * the directory we're cloning.
186 cmd = cmd.cat( " ." );
188 from_path_cursor.reset();
189 while (from_path_cursor.next()) {
190 cmd = cmd.cat( " " ).cat( *from_path_cursor );
193 cmd = cmd.cat( " |" );
194 if (_to_path_is_dir) {
195 cmd = cmd.cat( " ( cd " );
196 if (_clonedir_mode) {
197 char realpath_buf[ MAXPATHLEN ];
200 * If we're in _clonedir_mode, then we'll be
201 * cd'ing down into _from_path, and so we want
202 * a realpath of _to_path to cd over to,
203 * because if _from_path is a symlink then
204 * in _from_path "../_to_path" is _not_ _to_path.
206 char *real_to_path = _tt_get_realpath( (char *)_to_path,
208 if (real_to_path == NULL) {
209 fprintf( stderr, "%s: %s: %s\n",
210 (char *)_process_name,
211 (char *)_to_path, strerror(errno) );
214 cmd = cmd.cat( real_to_path );
216 cmd = cmd.cat( _to_path );
218 cmd = cmd.cat( " ;" );
220 cmd = cmd.cat( " tttar xfL" );
222 cmd = cmd.cat( "p" );
224 cmd = cmd.cat( " -" );
226 * Use the hack we built into tttar(1) to rename paths
227 * as they're extracted. Rename each _from_path to _to_path.
229 from_path_cursor.reset();
230 while (from_path_cursor.next()) {
231 _Tt_string from_path = *from_path_cursor;
232 cmd = cmd.cat( " -rename " ).cat( from_path )
233 .cat( " " ).cat( _to_path );
235 * If the copy is to be made in a subdirectory of _to_path,
236 * make tttar maps _from_path to the appropriate
237 * subdirectory of _to_path as it extracts.
239 if ((_to_path_is_dir) && (! _clonedir_mode)) {
242 .cat( from_path.rsplit( '/', dir_name ));
245 if (_to_path_is_dir) {
246 cmd = cmd.cat( " )" );
248 //printf( "Invoking: %s\n", (char *)cmd );
249 int sys_status = system( (char *)cmd );
250 #ifdef DO_TTTAR_AFTER_CP
251 if (_clonedir_mode) {
253 * remove the target directory, so that cp(1) won't
254 * see it and make _from_path a subdirectory of it.
256 if (rmdir( (char *)_to_path ) != 0) {
257 fprintf( stderr, "%s: rmdir(\"%s\"): %s\n",
258 (char *)_process_name, (char *)_to_path,
264 if (WIFEXITED(sys_status)) {
265 if (WEXITSTATUS(sys_status) == 0) {
268 return TT_ERR_INTERNAL;
272 "%s: system(\"%s\"): %d\n",
273 (char *)_process_name, sys_status );
274 return TT_ERR_INTERNAL;
280 * copier::can_cp() - Can we copy this path to _to_path?
282 * TO_DO: can_cp() can be as tricky as you like.
285 can_cp( _Tt_string from_path )
287 struct stat stat_buf;
289 if (stat( (char *)from_path, &stat_buf) != 0) {
292 if (S_ISDIR(stat_buf.st_mode) && (! _recurse)) {
304 char *process_id = tt_open();
305 Tt_status err = tt_ptr_error( process_id );
307 _process_id = process_id;
309 } else if (err > TT_WRN_LAST) {
311 "%s: tt_open(): %s\n",
312 (char *)_process_name, tt_status_message( err ) );
326 Tt_status err = tt_close();
327 if (err > TT_WRN_LAST) {
329 "%s: tt_close(): %s\n",
330 (char *)_process_name, tt_status_message( err ) );
336 * copier::parse_args()
339 parse_args( int argc, char **argv )
341 for ( int arg_num = 1; arg_num < argc; arg_num++ ) {
342 _Tt_string arg( argv[arg_num] );
343 _args->append( arg );
345 this->_parse_arg( (char *)arg );
347 if (arg_num == argc - 1) {
350 _from_paths->append( arg );
354 if ((_to_path.len() <= 0) || (_from_paths->count() <= 0)) {
358 if (_from_paths->count() > 1) {
360 * If multiple things to move, the place we're moving them to
361 * must be a directory.
363 struct stat stat_buf;
365 if (stat( (char *)_to_path, &stat_buf) != 0) {
366 fprintf( stderr, "%s: %s: ", (char *)_process_name,
371 if (! S_ISDIR(stat_buf.st_mode)) {
372 fprintf( stderr, "%s: %s: %s\n",
373 (char *)_process_name, (char *)_to_path,
378 _to_path_is_dir = TRUE;
380 struct stat stat_buf;
382 _to_path_is_dir = FALSE;
383 if (stat( (char *)_to_path, &stat_buf) == 0) {
384 _to_path_is_dir = S_ISDIR(stat_buf.st_mode);
387 * If you "cp -r dir1 dir2" and dir2 doesn't
388 * exist, cp(1) creates it and copies dir1's
389 * contents into it. What we need to do in this
391 * 1. mkdir( dir2 ), [so we can cd there]
392 * 2. (cd dir1; tttar cf - .) | (cd dir2; tttar xf -)
393 * 3. rmdir( dir2 ) [so cp(1) will do right thing]
395 _Tt_string from_path = _from_paths->top();
396 if ( (_from_paths->count() == 1)
397 && (stat( (char *)from_path, &stat_buf) == 0)
398 && S_ISDIR(stat_buf.st_mode))
400 _clonedir_mode = TRUE;
401 _to_path_is_dir = TRUE;
409 * copier::_parse_arg() - Parse an option
411 * If you add any options not supported by cp(1) and still expect
412 * it to be system()'d, fix ::do_cp() to pass cp(1) the right args.
415 _parse_arg( char *arg )
421 while (arg[++n] != '\0') {
444 _TT_PRINT_VERSIONS((char *)_prog_name)
459 usage(FILE *fs) const
462 catgets(_ttcatd, 8, 13,
463 "Usage: %s [-pL] file1 file2\n"
464 " %s [-prRL] path1 [path2 ...] dir\n"
467 (char *)_prog_name, (char *)_prog_name, (char *)_prog_name,
468 (char *)_prog_name );
470 catgets(_ttcatd, 8, 14,
471 "\t-L do not perform a cp(1)\n"
472 "\t-v print the version number and quit\n"
473 "\t-h print this message\n" ));