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