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