Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / tt / bin / tttar / archiver.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: archiver.C /main/3 1995/10/20 16:59:21 rswiston $                                                   
28 /* @(#)archiver.C       1.17 93/07/30 
29  * archiver.cc - ToolTalk wrapper for tar(1).
30  *
31  * Copyright (c) 1990 by Sun Microsystems, Inc.
32  *
33  */
34
35 #include "tt_options.h"
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/wait.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include "api/c/tt_c.h"
46 #include "util/copyright.h"
47 #include "util/tt_gettext.h"
48 #include "tttar_utils.h"
49 #include "tttar_file_utils.h"
50 #include "tttar_api.h"
51 #include "archiver.h"
52
53 /*
54  * External variables
55  */
56
57 /*
58  * archiver::archiver()
59  */
60 archiver::
61 archiver( char *arg0 )
62 {
63         if (arg0 != NULL) {
64                 char *base = strrchr( arg0, '/' );
65                 if (base == NULL) {
66                         base = arg0;
67                 } else {
68                         base++;
69                 }
70                 _prog_name = base;
71                 _process_name = _prog_name;
72         } else {
73                 _process_name = "(No process name)";
74                 _prog_name = "(No program name)";
75         }
76         _mode                   = NO_MODE;
77         _verbosity              = 0;
78         _follow_symlinks        = FALSE;
79         _recurse                = TRUE;
80         _archive_links          = TRUE;
81         _archive_external_links = TRUE;
82         _preserve_modes         = FALSE;
83         _preserve__props        = TRUE;
84         _should_tar             = TRUE;
85         _only_1_look_at_tarfile = FALSE;
86         _io_stream              = NULL;
87         _paths2tar              = new _Tt_string_list;
88         _renamings              = new Lstar_string_map_list;
89 }
90
91 archiver::
92 ~archiver()
93 {
94 }
95
96 /*
97  * archiver::do_tar() - Invoke tar(1) using system()
98  *      If _mode is CREATE,  do_tttarfile means "also archive tttarfile".
99  *      If _mode is EXTRACT, do_tttarfile means "only extract tttarfile".
100  */
101 int archiver::
102 do_tar( _Tt_string tttardir, bool_t do_tttarfile )
103 {
104         char            _curdir[ MAXPATHLEN+1 ];
105         _Tt_string      curdir;
106         int             status2return;
107         _Tt_string      cmd( "tar " );
108         _Tt_string      exclude_file;
109
110         /*
111          * Further down, we use the X option to exclude the tttarfile when
112          * extracting so we can extract it into a different place.
113          * Unfortunately AIX and HPUX don\'t have the X option, so
114          * their users will just have to suffer with having the extra
115          * tttarfile appear when doing extracting, and we hope we
116          * have permission to do so..
117          */
118
119 #if !defined(OPT_TAR_HAS_EXCLUDE_OPTION)
120         _only_1_look_at_tarfile = TRUE;
121 #endif  
122         /*
123          * If we're extracting just the tttarfile, chdir() to
124          * a place where we can extract it with permission and
125          * without collisions.
126          */
127         if (do_tttarfile && (_mode == EXTRACT)) {
128 #if !defined(OPT_BUG_SUNOS_4)
129                 curdir =  getcwd( _curdir, MAXPATHLEN );
130 #else
131                 curdir = getwd( _curdir );
132 #endif
133                 int status = chdir( (char *)tttardir );
134                 if (status != 0) {
135                         return status;
136                 }
137         }
138         switch (_mode) {
139             case CREATE:
140                 cmd = cmd.cat( "c" );
141                 break;
142             case EXTRACT:
143                 cmd = cmd.cat( "x" );
144                 break;
145             case LIST:
146                 cmd = cmd.cat( "t" );
147                 break;
148             case NO_MODE:
149             default:
150                 return 1;
151         }
152         for (int n = 0; n < _verbosity; n++) {
153                 cmd = cmd.cat( "v" );
154         }
155         if (_follow_symlinks) {
156                 cmd = cmd.cat( "h" );
157         }
158         if (_preserve_modes) {
159                 cmd = cmd.cat( "p" );
160         }
161         if (_tarfile_arg.len() != 0) {
162                 cmd = cmd.cat( "f" );
163         }
164         /*
165          * If we're extracting and we don't want the tttarfile, we
166          * need to pass the X option to exclude the tttarfile.
167          * But if we're extracting from stdin, we only get one chance
168          * to read the input, so in that case we can't exclude the
169          * tttarfile from being extracted.
170          */
171
172         if ((_mode == EXTRACT) && (! do_tttarfile) && (!_only_1_look_at_tarfile))
173         {
174                 cmd = cmd.cat( "X" );
175         }
176         if (_tarfile_arg.len() != 0) {
177                 cmd = cmd.cat( " " );
178                 /*
179                  * If we've chdir()'d to our temp dir to extract the
180                  * tttarfile, then be sure to prepend the old cwd
181                  * to the tarfile's name if it's relative.
182                  */
183                 if (    do_tttarfile
184                      && (_mode == EXTRACT)
185                      && (_tarfile_arg[0] != '/'))
186                 {
187                         cmd = cmd.cat( curdir ).cat( "/" );
188                 }
189                 cmd = cmd.cat( _tarfile_arg );
190         }
191         _Tt_string_list_cursor pathc( _paths2tar );
192         switch (_mode) {
193             case CREATE:
194                 while (pathc.next()) {
195                         cmd = cmd.cat( " " ).cat( *pathc );
196                 }
197                 /*
198                  * Have tar dip into our temporary directory and
199                  * pick up the tttarfile.
200                  */
201                 if (do_tttarfile) {
202                         cmd = cmd.cat( " -C " ).cat( tttardir ).
203                                 cat( " tttarfile" );
204                 }
205                 break;
206             case EXTRACT:
207                 if (do_tttarfile) {
208                         /*
209                          * Only extract the tttarfile.
210                          */
211                         cmd = cmd.cat( " tttarfile" );
212                 } else {
213                         if (_only_1_look_at_tarfile) {
214                                 /*
215                                  * If we're extracting from stdin, we
216                                  * can only invoke tar(1) once, so
217                                  * hope we have write permission
218                                  * on the current directory, 'cuz that's
219                                  * where we've got to put the tttarfile.
220                                  * Only explicitly ask for the tttarfile
221                                  * if we are being picky in what we extract.
222                                  */
223                                 if (_paths2tar->count() > 0) {
224                                         cmd = cmd.cat( " tttarfile" );
225                                 }
226                         } else {
227                                 /*
228                                  * Extract everything _but_ the tttarfile.
229                                  */
230                                 exclude_file = tttardir.cat( "/tar.exclude" );
231                                 FILE *fp = fopen( (char *)exclude_file, "w" );
232                                 if (fp == NULL) {
233                                         cmd = cmd.cat( " /dev/null" );
234                                 } else {
235                                         fprintf( fp, "tttarfile\n" );
236                                         fclose( fp );
237                                         cmd = cmd.cat( " " ).cat( exclude_file );
238                                 }
239                         }
240                         while (pathc.next()) {
241                                 cmd = cmd.cat( " " ).cat( *pathc );
242                         }
243                 }
244                 break;
245             case LIST:
246                 break;
247             case NO_MODE:
248             default:
249                 return 1;
250         }
251         //printf( "Invoking: %s\n", (char *)cmd );
252         int sys_status = system( (char *)cmd );
253         if (WIFEXITED(sys_status)) {
254                 status2return = WEXITSTATUS(sys_status);
255         } else {
256                 fprintf( stderr,
257                          "%s: system(\"%s\"): %d\n",
258                          (char *)_process_name, (char *)cmd, sys_status );
259                 status2return = 1;
260         }
261         if (_mode == EXTRACT) {
262                 int status;
263                 if (do_tttarfile) {
264                         status = chdir( (char *)curdir );
265                         if (status != 0) {
266                                 fprintf( stderr, "%s: chdir(\"%s\"): %s\n",
267                                         (char *)_process_name,
268                                         (char *)curdir, strerror(errno) );
269                                 exit( status );
270                         }
271                 } else if (exclude_file.len() > 0) {
272                         status = unlink( (char *)exclude_file );
273                         if (status != 0) {
274                                 perror( (char *)exclude_file );
275                         }
276                 }
277         }
278         return status2return;
279
280 } /* archiver::do_tar() */
281
282 /*
283  * archiver::do_tttar() - Perform just the LS/TT part of tttar
284  */
285 bool_t archiver::
286 do_tttar( char *tttarfile_name, bool_t silent )
287 {
288         char                   *process_id;
289         int                     first_ttmalloc;
290         XDR                     xdrs;
291         bool_t                  val2return = TRUE;
292         char                    _curdir[ MAXPATHLEN+1 ];
293         _Tt_string              curdir;
294
295         _io_stream = this->_open_io_stream( tttarfile_name, silent );
296         if (_io_stream == NULL) {
297                 return FALSE;
298         }
299         switch (_mode) {
300             case CREATE:
301                 xdrstdio_create( &xdrs, _io_stream, XDR_ENCODE );
302                 break;
303             case LIST:
304             case EXTRACT:
305                 xdrstdio_create( &xdrs, _io_stream, XDR_DECODE );
306                 break;
307             case NO_MODE:
308             default:
309                 fprintf( stderr, "%s: Archive_mode: %d\n",
310                          (char *)_process_name, (int)_mode );
311                 return FALSE;
312         }
313
314         /*
315          * Tooltalk setup
316          */
317         first_ttmalloc = tt_mark();
318         note_ptr_err( tt_open() );
319         if (IS_TT_ERR(err_noted)) {
320                 return FALSE;
321         }
322         process_id = ptr_returned;
323
324         switch (_mode) {
325             case CREATE:
326                 val2return = pathlist_lstt_archive(
327                                 _paths2tar, _recurse, _follow_symlinks,
328                                 _verbosity, &xdrs );
329                 break;
330             case LIST:
331                 val2return = pathlist_lstt_archive_list(_paths2tar, _verbosity,
332                                                         &xdrs);
333                 break;
334             case EXTRACT:
335 #if !defined(OPT_BUG_SUNOS_4)
336                 curdir = getcwd( _curdir, MAXPATHLEN );
337 #else
338                 curdir = getwd( _curdir );
339 #endif
340                 val2return = pathlist_lstt_dearchive( _paths2tar, _renamings,
341                                                       curdir, _preserve__props,
342                                                       _verbosity, &xdrs);
343                 break;
344             case NO_MODE:
345             default:
346                 break;
347         }
348         
349         /*
350          * Tooltalk teardown
351          */
352         note_err( tt_close() );
353         my_tt_release( first_ttmalloc );
354
355         xdr_destroy( &xdrs );
356         if ((_io_stream != stdin) && (_io_stream != stdout)) {
357                 fclose( _io_stream );
358         }
359         return val2return;
360 }
361
362 /*
363  * archiver::parse_args()
364  */
365 void archiver::
366 parse_args( int argc, char **argv )
367 {
368         bool_t  next_arg_is_tarfile     = FALSE;
369
370         for ( int argnum = 1; argnum < argc; argnum++ ) {
371                 char *cp = argv[argnum];
372                 _Tt_string arg( cp );
373                 if (argnum == 1) {
374                         /*
375                          * Process mode setting and other flags.
376                          */
377                         while (*cp != '\0') {
378                                 switch (*cp) {
379                                     case 'c':
380                                     case 't':
381                                     case 'x':
382                                         if (_mode == NO_MODE) {
383                                                 switch (*cp) {
384                                                     case 'c':
385                                                         _mode = CREATE;
386                                                         break;
387                                                     case 't':
388                                                         _mode = LIST;
389                                                         break;
390                                                     case 'x':
391                                                         _mode = EXTRACT;
392                                                         break;
393                                                 }
394                                         } else {
395                                                 this->usage();
396                                                 exit(1);
397                                         }
398                                         break;
399                                     case '-':
400                                         cp++;
401                                         switch (*cp) {
402                                             case 'v':
403                                                 _TT_PRINT_VERSIONS
404                                                           ((char *)_prog_name)
405                                                 exit(0);
406                                                 break;
407                                             case 'h':
408                                                 this->usage();
409                                                 exit(0);
410                                                 break;
411                                             default:
412                                                 this->usage();
413                                                 exit(1);
414                                                 break;
415                                         }
416                                         break;
417                                     case 'f':
418                                         next_arg_is_tarfile = TRUE;
419                                         break;
420                                     case 'R':
421                                         _recurse = FALSE;
422                                         break;
423                                     case 'h':
424                                         _follow_symlinks = TRUE;
425                                         break;
426                                     case 'p':
427                                         _preserve_modes = TRUE;
428                                         break;
429                                     case 'P':
430                                         _preserve__props = FALSE;
431                                         break;
432                                     case 'S':
433                                         _archive_links = FALSE;
434                                         break;
435                                     case 'E':
436                                         _archive_external_links = FALSE;
437                                         break;
438                                     case 'L':
439                                         _should_tar = FALSE;
440                                         break;
441                                     case 'v':
442                                         _verbosity++;
443                                         break;
444                                     default:
445                                         this->usage();
446                                         exit(1);
447                                 }
448                                 cp++;
449                         }
450                 } else if (next_arg_is_tarfile) {
451                         _tarfile_arg = cp;
452                         next_arg_is_tarfile = FALSE;
453                 } else if (arg == "-rename") {
454                         Lstar_string_map_ptr m = new Lstar_string_map;
455
456                         if (++argnum >= argc) {
457                                 this->usage();
458                                 exit(1);
459                         } else {
460                                 m->old_string_set( argv[argnum] );
461                                 if (++argnum >= argc) {
462                                         this->usage();
463                                         exit(1);
464                                 } else {
465                                         m->new_string_set( argv[argnum] );
466                                         _renamings->append( m );
467                                 }
468                         }
469                 } else {
470                         /*
471                          * Add this pathname to the list.
472                          */
473                         _Tt_string path = cp;
474                         _paths2tar->append( path );
475                 }
476         }
477         if (    ((_tarfile_arg.len() == 0) && (! _should_tar))
478              || (_mode == NO_MODE)
479              || (_should_tar && (! _recurse)))
480         {
481                 this->usage();
482                 exit(1);
483         }
484         _only_1_look_at_tarfile =
485                 (_tarfile_arg.len() == 0) || (_tarfile_arg == "-");
486         /*
487          * Renaming as you dearchive is not a tar(1) option.
488          */
489         if (    (_renamings->count() > 0)
490              && (_should_tar || (_mode != EXTRACT)))
491         {
492                 this->usage();
493                 exit(1);
494         }
495 }
496
497 /*
498  * archiver::_open_io_stream() - Returns an file pointer opened according
499  *      to _mode.  Uses stdin/stdout if filename is NULL or "-".
500  *      Exits with an error message if the _mode is not set,
501  *      or if the named file cannot be opened.
502  */
503 FILE * archiver::
504 _open_io_stream(char *filename, bool_t silent)
505 {
506         bool_t  in_pipe;
507         FILE   *fp = NULL;
508
509         in_pipe = (    (filename == NULL)
510                     || (strcmp( filename, "-" ) == 0));
511         switch (_mode) {
512             case CREATE:
513                 if (in_pipe) {
514                         return stdout;
515                 } else {
516                         fp = fopen( filename, "w" );
517                 }
518                 break;
519             case EXTRACT:
520             case LIST:
521                 if (in_pipe) {
522                         return stdin;
523                 } else {
524                         fp = fopen( filename, "r" );
525                 }
526                 break;
527             case NO_MODE:
528             default:
529                 fprintf( stderr, "%s: Archive_mode: %d\n",
530                          (char *)_process_name, (int)_mode );
531         }
532         if ((fp == NULL) && (! silent)) {
533                 fprintf( stderr, "%s: %s: %s\n",
534                          (char *)_process_name, filename, strerror(errno) );
535         }
536         return fp;
537
538 } /* archiver::_open_io_stream() */
539
540 /*
541  * archiver::usage()
542  */
543 void archiver::
544 usage( FILE *fs ) const
545 {
546         fprintf( fs,
547                  catgets(_ttcatd, 7, 2,
548                          "Usage: %s {ctx}[fhpPv[v]] [tarfile] pathname ...\n"
549                          "       %s {ctx}fL[hpPRv[v]] tttarfile pathname ...\n"
550                          "       %s -v\n"
551                          "       %s -h\n"),
552                  (char *)_prog_name, (char *)_prog_name, (char *)_prog_name,
553                  (char *)_prog_name );
554         fprintf( fs,
555                  catgets(_ttcatd, 7, 3,
556                          "\tc       create an archive\n"
557                          "\tt       list an archive's contents\n"
558                          "\tx       extract from an archive\n"
559                          "\tf       use next arg <tarfile> as archive\n"
560                          "\th       follow symbolic links\n"
561                          "\tL       do not invoke tar(1)\n"
562                          "\tp       preserve file modes\n"
563                          "\tP       (root) do not preserve objects' "
564                                     "owner, mod time, etc.\n"
565                          "\tR       do not recurse into directories\n"
566                          "\tv       be verbose\n"
567                          "\tvv      be very verbose\n"
568                          "\t-v      print the version number and quit\n"
569                          "\t-h[elp] print this message\n"));
570 }