/*============================================================================== Copyright (C) 2007 Martin Furter This file is part of svntar svntar is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. svntar is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with svntar; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ==============================================================================*/ #include "svntar_support.h" #include "svn_tar.h" #include "svn_tar_url.h" #include "svn_tar_wc.h" #include "svn_cmdline.h" #include "svn_config.h" #include "svn_fs.h" #include "svn_path.h" #define SVNTAR_VERSION "0.3.0" #ifdef SVN_IO_PIPE_STDIN #define SVN_HAS_SVN_IO_START_CMD2 #endif enum { opt_group_id = SVN_OPT_FIRST_LONGOPT_ID, opt_group_name, opt_user_id, opt_user_name, /* just end of list */ opt__none }; enum { opt_bit_revision = 1<<0, opt_bit_prefix = 1<<1, opt_bit_group_id = 1<<2, opt_bit_group_name = 1<<3, opt_bit_user_id = 1<<4, opt_bit_user_name = 1<<5, /* just end of list */ opt_bit__none }; const apr_getopt_option_t svn_tar_options[] = { /* general options */ { "help", 'h', 0, "print a help text" }, { "quiet", 'q', 0, "be quiet" }, { "revision", 'r', 1, "the revision" }, { "version", 'V', 0, "print the version" }, /* tar options */ { "prefix", 'p', 1, "prefix for the filenames" }, { "group-id", opt_group_id, 1, "" }, { "group-name", opt_group_name, 1, "" }, { "user-id", opt_user_id, 1, "" }, { "user-name", opt_user_name, 1, "" }, /* compression options */ { "bzip2", 'j', 0, "compress using bzip2" }, { "gzip", 'z', 0, "compress using gzip" }, { "lzma", 'l', 0, "compress using lzma" }, /* end of list */ { 0, 0, 0, 0 } }; typedef struct { svn_boolean_t quiet; } tar_options_t; typedef struct { apr_pool_t* pool; const char* filename; const char* compressor; svn_stream_t* stream; apr_proc_t cmd_proc; } tarstream_baton_t; static svn_error_t* tarstream_read( void* baton, char* buffer, apr_size_t* len ) { return svn_error_create( 0, NULL, _( "Can't read from tar file stream" ) ); } /* open the stream if not done yet, write to it */ static svn_error_t* tarstream_write( void* baton, const char* buffer, apr_size_t* len ) { tarstream_baton_t* tb = baton; if( !tb->stream ) { apr_status_t apr_err; svn_error_t* err; apr_file_t* file; if( tb->filename ) { SVN_ERR( svn_io_file_open( &file, tb->filename, APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY, APR_OS_DEFAULT, tb->pool ) ); } else { apr_err = apr_file_open_stdout( &file, tb->pool ); if( apr_err ) { return svn_error_wrap_apr( apr_err, _( "Could not open stdout" ) ); } } if( tb->compressor ) { const char* args[2]; args[0] = tb->compressor; args[1] = NULL; err = svntar_start_cmd( &tb->cmd_proc, args, file, tb->pool ); if( err ) { apr_file_close( file ); return err; } file = tb->cmd_proc.in; } tb->stream = svn_stream_from_aprfile2( file, FALSE, tb->pool ); } return svn_stream_write( tb->stream, buffer, len ); } /* */ static svn_error_t* tarstream_close( void* baton ) { tarstream_baton_t* tb = baton; if( tb->stream ) { SVN_ERR( svn_stream_close( tb->stream ) ); if( tb->compressor ) { int exitcode; apr_exit_why_e exitwhy; SVN_ERR( svn_io_wait_for_cmd( &tb->cmd_proc, tb->compressor, &exitcode, &exitwhy, tb->pool ) ); } } return SVN_NO_ERROR; } static svn_stream_t* tarstream_create( const char* filename, const char* compressor, apr_pool_t* pool ) { tarstream_baton_t* tb; svn_stream_t* stream; tb = apr_palloc( pool, sizeof(* tb ) ); tb->pool = pool; tb->filename = filename; tb->compressor = compressor; tb->stream = 0; stream = svn_stream_create( tb, pool ); svn_stream_set_read( stream, tarstream_read ); svn_stream_set_write( stream, tarstream_write ); svn_stream_set_close( stream, tarstream_close ); return stream; } static svn_error_t* tar_add_callback( void* baton, char type, const char* name, apr_pool_t* pool ) { // +++ need to write to stderr if tar goes to stdout +++ printf( " %c %s\n", type, name ); return SVN_NO_ERROR; } static void print_help() { printf( "\n" "usage: svntar [options] tarfile source\n" "\n" " tarfile: name of the tar file to create.\n" " source: working copy path or repos URL.\n" "\n" " options:\n" "\n" " -h [--help] : print this help text\n" " -q [--quiet] : print as little as possible\n" " -r rev : revision number\n" " -V [--version] : print the version only\n" " -p [--prefix] path : prepend filenames with this path\n" " --group-id nr : set group id\n" " --group-name name : set group name\n" " --user-id nr : set user id\n" " --user-name name : set user name\n" " -j [--bzip2] : compress using bzip2\n" " -z [--gzip] : compress using gzip\n" " -l [--lzma] : compress using lzma\n" "\n" ); } static void print_version() { printf( "svntar " SVNTAR_VERSION "\n" ); } static svn_error_t* svn_main( int* returncode, int argc, const char** argv ) { apr_pool_t* pool; apr_getopt_t* os; svn_opt_revision_t rev; svn_opt_revision_t peg; svn_client_ctx_t* ctx; svn_tar_t* tar; const char* tarfilename; const char* sourcepath; const char* truesourcepath; const char* compressor; tar_options_t tar_opts; unsigned int opt_bits; /* Initialize the app. Send all error messages to 'stderr'. */ if( svn_cmdline_init( "svntar", stderr )!= EXIT_SUCCESS ) { *returncode = EXIT_FAILURE; return SVN_NO_ERROR; } /* Create top-level memory pool. Be sure to read the HACKING file to * understand how to properly use/free subpools. */ pool = svn_pool_create( NULL ); /* Initialize the FS library. */ SVN_ERR( svn_fs_initialize( pool ) ); /* initialize */ tar_opts.quiet = FALSE; compressor = NULL; peg.kind = svn_opt_revision_unspecified; rev.kind = svn_opt_revision_unspecified; SVN_ERR( svn_tar_create( &tar, pool ) ); /* process commandline */ SVN_ERR( svn_cmdline__getopt_init( &os, argc, argv, pool ) ); os->interleave = 1; opt_bits = 0; while( 1 ) { apr_status_t apr_err; int opt_id; const char* opt_arg; apr_err = apr_getopt_long( os, svn_tar_options, &opt_id, &opt_arg ); if( APR_STATUS_IS_EOF( apr_err ) ) { break; } else if( apr_err ) { // svn_cl__help( NULL, NULL, pool ); svn_pool_destroy( pool ); *returncode = EXIT_FAILURE; return SVN_NO_ERROR; } switch( opt_id ) { /* subversion and general options */ case 'h': print_help( ); return 0; case 'q': if( tar_opts.quiet ) { return svn_error_create( 0, NULL, "Option -q specified twice" ); } tar_opts.quiet = TRUE; break; case 'r': if( rev.kind != svn_opt_revision_unspecified ) { return svn_error_create( 0, NULL, "Option -r specified twice" ); } else { svn_opt_revision_t endrev; endrev.kind = svn_opt_revision_unspecified; if( svn_opt_parse_revision( &rev, &endrev, opt_arg, pool ) != 0 ) { SVN_ERR( svn_utf_cstring_to_utf8( &opt_arg, opt_arg, pool ) ); return svn_error_createf( 0, NULL, _( "Syntax error in revision argument '%s'" ), opt_arg ); } else if( endrev.kind != svn_opt_revision_unspecified ) { return svn_error_create( 0, NULL, _( "Revision ranges are not supported" ) ); } opt_bits |= opt_bit_revision; } break; case 'V': print_version(); return 0; /* tar options */ case 'p': if( opt_bits & opt_bit_prefix ) { return svn_error_create( 0, NULL, "Option -p specified twice" ); } opt_bits |= opt_bit_prefix; svn_tar_set_prefix( tar, opt_arg, pool ); break; case opt_group_id: if( opt_bits & opt_bit_group_id ) { return svn_error_create( 0, NULL, _( "Option --group-id specified twice" ) ); } else { svn_tar_set_group_id( tar, atoi( opt_arg ) ); opt_bits |= opt_bit_group_id; } break; case opt_group_name: if( opt_bits & opt_bit_group_name ) { return svn_error_create( 0, NULL, _( "Option --group-name specified twice" ) ); } else { SVN_ERR( svn_tar_set_group_name( tar, opt_arg ) ); opt_bits |= opt_bit_group_name; } break; case opt_user_id: if( opt_bits & opt_bit_user_id ) { return svn_error_create( 0, NULL, _( "Option --user-id specified twice" ) ); } else { svn_tar_set_user_id( tar, atoi( opt_arg ) ); opt_bits |= opt_bit_user_id; } break; case opt_user_name: if( opt_bits & opt_bit_user_name ) { return svn_error_create( 0, NULL, _( "Option --user-name specified twice" ) ); } else { SVN_ERR( svn_tar_set_user_name( tar, opt_arg ) ); opt_bits |= opt_bit_user_name; } break; /* compression options */ case 'j': if( compressor ) { return svn_error_create( 0, NULL, _( "Multiple compression options specified" ) ); } else { compressor = "bzip2"; } break; case 'z': if( compressor ) { return svn_error_create( 0, NULL, _( "Multiple compression options specified" ) ); } else { compressor = "gzip"; } break; case 'l': if( compressor ) { return svn_error_create( 0, NULL, _( "Multiple compression options specified" ) ); } else { compressor = "lzma"; } break; default: return svn_error_createf( 0, NULL, "Unhandled option %d", opt_id ); } } /* Fill out a client_ctx object. */ SVN_ERR( svntar_init_client_context( &ctx, pool ) ); if( os->ind !=( os->argc - 2 ) ) { return svn_error_create( 0, NULL, "need tarfilename and path or URL" ); } tarfilename = os->argv[os->ind]; sourcepath = os->argv[os->ind+1]; if( strcmp( tarfilename, "-" ) == 0 ) { /* output to stdout... */ tarfilename = 0; } /* Now do the real work. */ svn_tar_set_output( tar, tarstream_create( tarfilename, compressor, pool ) ); svn_tar_set_add_callback( tar, tar_add_callback, NULL ); SVN_ERR( svn_opt_parse_path( &peg, &truesourcepath, sourcepath, pool ) ); if( svn_path_is_url( sourcepath ) || ( peg.kind != svn_opt_revision_unspecified && peg.kind != svn_opt_revision_base ) || ( rev.kind != svn_opt_revision_unspecified && rev.kind != svn_opt_revision_committed && rev.kind != svn_opt_revision_base ) ) { if( rev.kind == svn_opt_revision_unspecified ) { rev.kind = svn_opt_revision_head; } SVN_ERR( svn_tar_url( tar, truesourcepath, &peg, &rev, ctx, pool ) ); } else { svn_boolean_t baserev; baserev = rev.kind == svn_opt_revision_base ? TRUE : FALSE; SVN_ERR( svn_tar_wc( tar, sourcepath, baserev, ctx, pool ) ); } SVN_ERR( svn_tar_close( tar ) ); return SVN_NO_ERROR; } int main( int argc, const char** argv ) { int returncode = EXIT_SUCCESS; svn_error_t* err; err = svn_main( &returncode, argc, argv ); if( err ) { svn_handle_error2( err, stderr, FALSE, "svntar: " ); returncode = EXIT_FAILURE; } return returncode; }