0d1377e11b829060b14705c27e89f1f679209c04
[oweals/cde.git] / cde / lib / tt / bin / shell / mover.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
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: mover.C /main/4 1995/10/20 16:36:15 rswiston $                                                      
28 /*
29  * mover.cc - Link Service/ToolTalk wrapper for mv(1).
30  *
31  * Copyright (c) 1990 by Sun Microsystems, Inc.
32  *
33  */
34
35 #include "tt_options.h"
36 #include <errno.h>
37 #include <string.h>
38 #if defined(__osf__) || defined(linux) || defined(CSRG_BASED)
39 #include <unistd.h>
40 #else
41 #if !defined(USL) && !defined(__uxp__) && !defined(sun)
42 #include <osfcn.h>
43 #endif
44 #endif
45 #include <stdlib.h>
46 #include <errno.h>
47 #if defined(ultrix)
48 #include <sys/inode.h>
49 #define S_ISLNK(m)     (((m)&IFMT) == IFLNK)
50 #endif
51 #include <sys/stat.h>
52 #if defined(OPT_BUG_USL) || defined(OPT_BUG_UXP)
53 #define S_ISLNK(mode)     (((mode) & S_IFMT) == S_IFLNK)
54 #endif
55 #include <sys/wait.h>
56 #include "api/c/tt_c.h"
57 #include "util/tt_gettext.h"
58 #include "util/copyright.h"
59 #include "mover.h"
60
61 /*
62  * External variables
63  */
64
65 /*
66  * mover::mover()
67  */
68 mover::
69 mover( char *arg0 )
70 {
71         if (arg0 != NULL) {
72                 char *base = strrchr( arg0, '/' );
73                 if (base == NULL) {
74                         base = arg0;
75                 } else {
76                         base++; // Don't want the '/'
77                 }
78                 _prog_name = base;
79                 _process_name = _prog_name;
80         }
81         _args = new _Tt_string_list();
82         _from_paths = new _Tt_string_list();
83         _should_mv = TRUE;
84         _force = FALSE;
85         _tt_opened = FALSE;
86         _to_path_is_dir = FALSE;
87 }
88
89 mover::
90 ~mover()
91 {
92 }
93
94 /*
95  * mover::do_mv() - Use system() to invoke mv(1), and return its exit status.
96  *      We can just use _args, since we never get here when our one
97  *      mv-incompatible option (-L) has been given.
98  */
99 int mover::
100 do_mv()
101 {
102         _Tt_string              cmd( "mv" );
103         _Tt_string_list_cursor  arg_cursor( _args );
104
105         while (arg_cursor.next()) {
106                 cmd = cmd.cat( " " ).cat( *arg_cursor );
107         }
108         //printf( "Invoking: %s\n", (char *)cmd );
109         int sys_status = system( (char *)cmd );
110         if (WIFEXITED(sys_status)) {
111                 return WEXITSTATUS(sys_status);
112         } else {
113                 if (! _force) {
114                         fprintf(stderr, "%s: system(\"%s\"): %d\n",
115                                 (char *)_process_name, (char *)cmd, sys_status);
116                 }
117                 return 1;
118         }
119 }
120
121 /*
122  * mover::do_ttmv() - Use tt_file_move() on the things to move.
123  */
124 Tt_status mover::
125 do_ttmv()
126 {
127         Tt_status       worst_err = TT_OK;
128         Tt_status       err;
129         _Tt_string      full_to_path;
130         bool_t          abort = FALSE;
131         bool_t          are_more;
132
133         full_to_path = _to_path;
134         _Tt_string_list_cursor from_path_cursor( _from_paths );
135         /*
136          * call to next() must be first, so that are_more will be valid
137          * if we abort.  Why does the next() method wrap around?
138          */
139         while ((are_more = from_path_cursor.next()) && (! abort)) {
140                 if (! this->can_mv( *from_path_cursor )) {
141                         continue;
142                 }
143                 /*
144                  * tt_file_destroy() any path that mv(1) will delete
145                  */
146                 if (_to_path_is_dir) {
147                         full_to_path = _to_path.cat("/").
148                                        cat(*from_path_cursor);
149                 }
150                 /*
151                  * mv(1) will overwrite any entry in _to_path that
152                  * has the same name as a _from_path.
153                  */
154                 err = tt_file_destroy( (char *)full_to_path );
155                 if ((err > TT_WRN_LAST) && (! _force)) {
156                         fprintf( stderr,
157                                  catgets(_ttcatd, 8, 2,
158                                          "%s: Could not remove "
159                                          "ToolTalk objects of %s "
160                                          "because %s\n"),
161                                  (char *)_process_name,
162                                  (char *)full_to_path,
163                                  tt_status_message(err) );
164                 }
165                 err = tt_file_move( (char *)*from_path_cursor,
166                                     (char *)full_to_path );
167                 if (err > TT_WRN_LAST) {
168                         worst_err = err;
169                         if (! _force) {
170                                 fprintf( stderr,
171                                          catgets(_ttcatd, 8, 3,
172                                                  "%s: Could not move ToolTalk "
173                                                  "objects of \"%s\" to \"%s\" "
174                                                  "because %s\n"),
175                                          (char *)_process_name,
176                                          (char *)*from_path_cursor,
177                                          (char *)full_to_path,
178                                          tt_status_message( err ));
179                         }
180                         switch (err) {
181                             case TT_ERR_DBAVAIL:
182                             case TT_ERR_PATH:
183                                 break;
184                             case TT_ERR_NOMP:
185                             case TT_ERR_DBEXIST:
186                             default:
187                                 abort = TRUE;
188                                 break;
189                         }
190                 }
191         }
192         if (are_more && (! _force)) {
193                 from_path_cursor.prev();
194                 fprintf( stderr,
195                          catgets(_ttcatd, 8, 4,
196                                  "%s: Will not attempt to move the ToolTalk "
197                                  "objects of:\n"),
198                          (char *)_process_name );
199                 while (from_path_cursor.next()) {
200                         fprintf( stderr, "\t%s\n", (char *)*from_path_cursor );
201                 }
202         }
203         /*
204          * TO_DO: This should be uncommented if you think that warning them
205          * about hygiene is more important than obeying the -f flag.
206          *
207         if ((worst_err > TT_WRN_LAST) && _should_mv && _force) {
208                 fprintf( stderr, "%s: The ToolTalk objects of some files were "
209                          "not moved.\nSince you've told us to move the files "
210                          "anyway, you will need to\nuse %s -L to move "
211                          "the ToolTalk objects of the problem files.\n",
212                          (char *)_process_name, (char *)_prog_name );
213         }
214         */
215         return worst_err;
216
217 } /* do_ttmv() */
218
219 /*
220  * mover::can_mv() - Can we move this path to _to_path?
221  *
222  *      TO_DO: Judging by mv.c, can_mv() can be as tricky as you like.
223  *      I'll count on tt_file_move() to Do The Right Thing.
224  */
225 bool_t mover::
226 can_mv( _Tt_string from_path )
227 {
228         struct stat lstat_buf;
229         if (lstat( (char *)from_path, &lstat_buf) == 0) {
230                 if (S_ISLNK(lstat_buf.st_mode)) {
231                         /*
232                          * Don't tt_file_move() a symlink, or TT will
233                          * tt_file_move() the linked file.
234                          */
235                         return FALSE;
236                 } else {
237                         return TRUE;
238                 }
239         } else {
240                 /*
241                  * If we're trying to mv a file that doesn't exist,
242                  * let's not tt_file_move() the associated pathname.
243                  * But if we're trying to ttmv -L a file that doesn't
244                  * exist, we should probably tt_file_move() it anyway.
245                  */
246                 if (_should_mv) {
247                         return FALSE;
248                 } else {
249                         return TRUE;
250                 }
251         }
252 }
253
254 /*
255  * mover::open_tt()
256  */
257 Tt_status mover::
258 open_tt()
259 {
260         char *process_id = tt_open();
261         Tt_status err = tt_ptr_error( process_id );
262         if (err == TT_OK) {
263                 _process_id = process_id;
264                 _tt_opened = TRUE;
265         } else if (err > TT_WRN_LAST) {
266                 fprintf( stderr,
267                          "%s: tt_open(): %s\n",
268                          (char *)_process_name, tt_status_message(err) );
269         }
270         return err;
271 }
272
273 /*
274  * mover::close_tt()
275  */
276 Tt_status mover::
277 close_tt()
278 {
279         if (! _tt_opened) {
280                 return TT_OK;
281         }
282         Tt_status err = tt_close();
283         if (err > TT_WRN_LAST) {
284                 fprintf( stderr,
285                          "%s: tt_close(): %s\n",
286                          (char *)_process_name, tt_status_message(err) );
287         }
288         return err;
289 }
290
291 /*
292  * mover::parse_args()
293  */
294 void mover::
295 parse_args( int argc, char **argv )
296 {
297         bool_t no_more_options = FALSE;
298
299         for ( int arg_num = 1; arg_num < argc; arg_num++ ) {
300                 _Tt_string arg( argv[arg_num] );
301                 _args->append( arg );
302                 if ((arg[0] == '-') && (! no_more_options)) {
303                         if (arg[1] == '\0') {
304                                 /*
305                                  * The bare option "-" means take the
306                                  * subsequent arguments to be pathnames.
307                                  */
308                                 no_more_options = TRUE;
309                         } else {
310                                 for (int n = 1; n < arg.len(); n++) {
311                                         switch (arg[n]) {
312                                             case 'f':
313                                                 _force = TRUE;
314                                                 break;
315                                             case 'L':
316                                                 _should_mv = FALSE;
317                                                 break;
318                                             case 'v':
319                                                 _TT_PRINT_VERSIONS((char *)_prog_name)
320                                                 exit(0);
321                                                 break;
322                                             case 'h':
323                                             default:
324                                                 this->usage();
325                                                 exit(1);
326                                         }
327                                 }
328                         }
329                 } else {
330                         if (arg_num == argc - 1) {
331                                 _to_path = arg;
332                         } else {
333                                 _from_paths->append( arg );
334                         }
335                 }
336         }
337         if ((_to_path.len() <= 0) || (_from_paths->count() <= 0)) {
338                 this->usage();
339                 exit(1);
340         }
341         if (_from_paths->count() > 1) {
342                 /*
343                  * If multiple things to move, the place we're moving them to
344                  * must be a directory.
345                  */
346                 struct stat stat_buf;
347
348                 if (stat( (char *)_to_path, &stat_buf) != 0) {
349                         fprintf( stderr, "%s: \"%s\": ", (char *)_process_name,
350                                  (char *)_to_path );
351                         perror(NULL);
352                         exit(2);
353                 }
354                 if (! S_ISDIR(stat_buf.st_mode)) {
355                         fprintf( stderr, "%s: \"%s\": %s\n",
356                                  (char *)_process_name, (char *)_to_path,
357                                  strerror(ENOTDIR) );
358                         this->usage();
359                         exit(2);
360                 }
361                 _to_path_is_dir = TRUE;
362         } else {
363                 struct stat stat_buf;
364
365                 _to_path_is_dir = FALSE;
366                 if (stat( (char *)_to_path, &stat_buf) == 0) {
367                         _to_path_is_dir = S_ISDIR(stat_buf.st_mode);
368                 }
369         }
370 }
371
372 /*
373  * mover::usage()
374  */
375 void mover::
376 usage(FILE *fs) const
377 {
378         fprintf( fs,
379                  catgets(_ttcatd, 8, 5,
380                          "Usage: %s [-] [-fL] path1 path2\n"
381                          "       %s [-] [-fL] path1 [path2 ...] dir\n"
382                          "       %s -v\n"
383                          "       %s -h\n"),
384                  (char *)_prog_name, (char *)_prog_name, (char *)_prog_name,
385                  (char *)_prog_name );
386         fprintf( fs, "%s",
387                  catgets(_ttcatd, 8, 6,
388                          "\t-L      do not perform a mv(1)\n"
389                          "\t-v      print the version number and quit\n"
390                          "\t-h      print this message\n" ));
391 }