From 8ba278586b953f7556e90f93c9bd9ec0fcc4c1d1 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Mon, 6 Feb 2017 12:21:34 -0500 Subject: [PATCH] Prepare minitar-cli 0.6 - Add unit tests and test on both appveyor and travis --- .gitignore | 2 + .hoerc | 52 +- .rubocop.yml | 106 ++++ .simplecov-prelude.rb | 13 + .travis.yml | 27 +- Contributing.md | 71 +++ Contributing.rdoc | 64 --- Gemfile | 24 +- History.md | 21 + History.rdoc | 17 - Licence.rdoc => Licence.md | 11 +- Manifest.txt | 22 +- README.rdoc | 9 +- Rakefile | 57 +- appveyor.yml | 29 + bin/minitar | 3 + lib/minitar-cli.rb | 3 - lib/minitar/cli.rb | 641 ++--------------------- lib/minitar/cli/command.rb | 43 ++ lib/minitar/cli/command/create.rb | 123 +++++ lib/minitar/cli/command/extract.rb | 157 ++++++ lib/minitar/cli/command/help.rb | 52 ++ lib/minitar/cli/command/list.rb | 159 ++++++ lib/minitar/cli/commander.rb | 59 +++ minitar-cli.gemspec | 50 +- test/fixtures/bad-dir.tar.gz | Bin 0 -> 119 bytes test/fixtures/spaces.tar.gz | Bin 0 -> 1340 bytes test/minitest_helper.rb | 88 +--- test/support/command_line_test_helper.rb | 11 + test/test_cli_help.rb | 71 +++ test/test_cli_list.rb | 218 ++++++++ 31 files changed, 1325 insertions(+), 878 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .simplecov-prelude.rb create mode 100644 Contributing.md delete mode 100644 Contributing.rdoc create mode 100644 History.md delete mode 100644 History.rdoc rename Licence.rdoc => Licence.md (60%) create mode 100644 appveyor.yml delete mode 100644 lib/minitar-cli.rb create mode 100644 lib/minitar/cli/command.rb create mode 100644 lib/minitar/cli/command/create.rb create mode 100644 lib/minitar/cli/command/extract.rb create mode 100644 lib/minitar/cli/command/help.rb create mode 100644 lib/minitar/cli/command/list.rb create mode 100644 lib/minitar/cli/commander.rb create mode 100644 test/fixtures/bad-dir.tar.gz create mode 100644 test/fixtures/spaces.tar.gz create mode 100644 test/support/command_line_test_helper.rb create mode 100644 test/test_cli_help.rb create mode 100644 test/test_cli_list.rb diff --git a/.gitignore b/.gitignore index 051f986..c065e9c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ html pkg publish test/cache.tst +tmp/ +.byebug_history diff --git a/.hoerc b/.hoerc index 75d7e96..cd32cc1 100644 --- a/.hoerc +++ b/.hoerc @@ -1,20 +1,42 @@ --- exclude: !ruby/regexp '/ - \.(tmp|swp)$ - | - CVS/ - | - (?i:TAGS) - | - \.(svn|git|hg|DS_Store|idea|vagrant)\/ - | - Gemfile(?:\.lock)? - | - type-lists\/ - | - \.(coveralls|pullreview|travis).yml$ - | - \.gemspec + \.(?: + tmp | + swp + )$ + | + \.(?: + autotest | + byebug_history | + gemtest | + gitignore | + hoerc | + simplecov-prelude.rb + )$ + | + (?: + appveyor | + coveralls | + pullreview | + rubocop | + travis + )\.yml$ + | + \b(?i:TAGS)$ + | + \.(?: + DS_Store | + bundle | + git | + hg | + idea | + svn | + vagrant + )\/ + | + [gG]emfile(?:\.lock)? + | + \.gemspec$ | Vagrantfile /x' diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..425dc18 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,106 @@ +--- +AllCops: + DisplayCopNames: true + DisplayStyleGuide: true + ExtraDetails: true + +Style/AlignHash: + EnforcedColonStyle: key + EnforcedHashRocketStyle: key + EnforcedLastArgumentHashStyle: enforce_implicit + +Style/AlignParameters: + EnforcedStyle: with_fixed_indentation + +Style/AndOr: + Enabled: false + +Style/AsciiComments: + Enabled: false + +Style/AsciiIdentifiers: + Enabled: false + +Style/BarePercentLiterals: + EnforcedStyle: percent_q + +Style/BlockDelimiters: + EnforcedStyle: semantic + IgnoredMethods: + - assert_raises + ProceduralMethods: + - spec + +Style/BracesAroundHashParameters: + EnforcedStyle: context_dependent + +Style/ClassAndModuleChildren: + Enabled: false + +Style/ClassCheck: + EnforcedStyle: kind_of? + +Style/CommandLiteral: + EnforcedStyle: percent_x + +Style/CommentAnnotation: + Enabled: true + +Style/Copyright: + Enabled: false + +Style/DotPosition: + EnforcedStyle: trailing + +Style/DoubleNegation: + Enabled: false + +Style/Encoding: # For a gem that is using Ruby 1.9, change this to true. + Enabled: false + +Style/FileName: + Exclude: + - lib/mime-types.rb + +Style/FormatString: + EnforcedStyle: percent + +Style/MultilineBlockChain: + Enabled: false + +Style/MultilineOperationIndentation: + EnforcedStyle: indented + +Style/ParallelAssignment: + Enabled: false + +Style/RegexpLiteral: + Enabled: false + +Style/SpaceInsideBrackets: + Enabled: false + +Style/UnneededPercentQ: + Enabled: false + +# This is one of the hobgoblins. +Metrics/AbcSize: + Enabled: false + +# This is one of the hobgoblins. +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/LineLength: + Exclude: + - Rakefile + +# This is one of the hobgoblins. +Metrics/MethodLength: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false diff --git a/.simplecov-prelude.rb b/.simplecov-prelude.rb new file mode 100644 index 0000000..5ed413f --- /dev/null +++ b/.simplecov-prelude.rb @@ -0,0 +1,13 @@ +require 'psych' if ENV['CI'] +require 'simplecov' + +if ENV['CI'] + require 'coveralls' + SimpleCov.formatter = Coveralls::SimpleCov::Formatter +end + +SimpleCov.start do + command_name 'Minitest' +end + +gem 'minitest' diff --git a/.travis.yml b/.travis.yml index acc7742..c12be97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,34 @@ --- language: ruby +env: + - DEV=1 rvm: - - 2.1.0 + - 2.4.0 + - 2.3.3 + - 2.2.6 + - 2.1.9 - 2.0.0 + - jruby-9.0.5.0 + - jruby-9.1.6.0 - 1.9.3 - 1.8.7 - ree - ruby-head - jruby-19mode - - jruby-head - - rbx-2 matrix: allow_failures: - - rvm: rbx-2 - - rvm: jruby-head - rvm: ruby-head - rvm: 1.8.7 - rvm: ree gemfile: - Gemfile +before_install: + - pushd .. && git clone https://github.com/halostatue/minitar.git && popd before_script: - - | - case "${TRAVIS_RUBY_VERSION}" in - rbx*) - gem install psych - ;; - esac - - rake travis:before -t -script: rake travis + - bundle exec rake travis:before -t +script: bundle exec rake travis after_script: - - rake travis:after -t + - bundle exec rake travis:after -t notifications: email: recipients: diff --git a/Contributing.md b/Contributing.md new file mode 100644 index 0000000..d2025be --- /dev/null +++ b/Contributing.md @@ -0,0 +1,71 @@ +## Contributing + +I value any contribution to minitar-cli you can provide: a bug report, a +feature request, or code contributions. There are a few guidelines for +contributing to minitar: + +* Code changes *will not* be accepted without tests. The test suite is + written with [Minitest][]. +* Match my coding style. +* Use a thoughtfully-named topic branch that contains your change. Rebase + your commits into logical chunks as necessary. +* Use [quality commit messages][]. +* Do not change the version number; when your patch is accepted and a release + is made, the version will be updated at that point. +* Submit a GitHub pull request with your changes. +* New or changed behaviours require appropriate documentation. + +### Test Dependencies + +minitar-cli uses Ryan Davis’s [Hoe][] to manage the release process, and it +adds a number of rake tasks. You will mostly be interested in: + + $ rake + +which runs the tests the same way that: + + $ rake test + $ rake travis + +will do. + +To assist with the installation of the development dependencies for +minitar-cli, I have provided the simplest possible Gemfile pointing to the +(generated) `minitar-cli.gemspec` file. This will permit you to do: + + $ bundle install + +to get the development dependencies. If you aleady have `hoe` installed, you +can accomplish the same thing with: + + $ rake newb + +This task will install any missing dependencies, run the tests/specs, and +generate the RDoc. + +You can run tests with code coverage analysis by running: + + $ rake test:coverage + +### Workflow + +Here's the most direct way to get your work merged into the project: + +* Fork the project. +* Clone down your fork (`git clone git://github.com//minitar-cli.git`). +* Create a topic branch to contain your change (`git checkout -b + my_awesome_feature`). +* Hack away, add tests. Not necessarily in that order. +* Make sure everything still passes by running `rake`. +* If necessary, rebase your commits into logical chunks, without errors. +* Push the branch up (`git push origin my_awesome_feature`). +* Create a pull request against halostatue/minitar-cli and describe what your + change does and the why you think it should be merged. + +### Contributors + +* Austin Ziegler created minitar-cli, extracted from Archive::Tar::Minitar. + +[Minitest]: https://github.com/seattlerb/minitest +[quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[Hoe]: https://github.com/seattlerb/hoe diff --git a/Contributing.rdoc b/Contributing.rdoc deleted file mode 100644 index 201290e..0000000 --- a/Contributing.rdoc +++ /dev/null @@ -1,64 +0,0 @@ -== Contributing - -I value any contribution to minitar-cli you can provide: a bug report, a -feature request, or code contributions. - -As minitar is a mature codebase, there are a few guidelines: - -* Code changes *will* *not* be accepted without tests. The test suite is - written with {Minitest}[https://github.com/seattlerb/minitest]. -* Match my coding style. -* Use a thoughtfully-named topic branch that contains your change. Rebase your - commits into logical chunks as necessary. -* Use {quality commit messages}[http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html]. -* Do not change the version number; when your patch is accepted and a release - is made, the version will be updated at that point. -* Submit a GitHub pull request with your changes. -* New or changed behaviours require new or updated documentation. - -=== Test Dependencies - -minitar-cli uses Ryan Davis’s {Hoe}[https://github.com/seattlerb/hoe] to manage -the release process, and it adds a number of rake tasks. You will mostly be -interested in: - - $ rake - -which runs the tests the same way that: - - $ rake test - $ rake travis - -will do. - -To assist with the installation of the development dependencies for -minitar-cli, I have provided the simplest possible Gemfile pointing to the -(generated) +minitar-cli.gemspec+ file. This will permit you to do: - - $ bundle install - -to get the development dependencies. If you aleady have +hoe+ installed, you -can accomplish the same thing with: - - $ rake newb - -This task will install any missing dependencies, run the tests/specs, and -generate the RDoc. - -=== Workflow - -Here's the most direct way to get your work merged into the project: - -* Fork the project. -* Clone down your fork (git clone git://github.com//minitar-cli.git). -* Create a topic branch to contain your change (git checkout -b my\_awesome\_feature). -* Hack away, add tests. Not necessarily in that order. -* Make sure everything still passes by running +rake+. -* If necessary, rebase your commits into logical chunks, without errors. -* Push the branch up (git push origin my\_awesome\_feature). -* Create a pull request against halostatue/minitar-cli and describe what your - change does and the why you think it should be merged. - -=== Contributors - -* Austin Ziegler created minitar-cli, extraced from Archive::Tar::Minitar. diff --git a/Gemfile b/Gemfile index 359a82d..576ea91 100644 --- a/Gemfile +++ b/Gemfile @@ -3,11 +3,27 @@ # NOTE: This file is present to keep Travis CI happy. Edits to it will not # be accepted. -source "https://rubygems.org/" +source 'https://rubygems.org/' -if RUBY_VERSION < "1.9" - gem 'mime-types', '~> 1.25' -end +mime_version = + if RUBY_VERSION < '1.9' + gem 'rdoc', '< 4.0' + gem 'rake', '~> 10.0' + '1.25' + elsif RUBY_VERSION < '2.0' + '2.0' + elsif RUBY_VERSION >= '2.0' + if RUBY_ENGINE == 'ruby' + gem 'byebug' if ENV['DEV'] + gem 'simplecov', '~> 0.7' + gem 'coveralls', '~> 0.7' + end + '3.0' + end + +gem 'mime-types', "~> #{mime_version}" + +gem 'minitar', :path => '../minitar' if ENV['DEV'] gemspec diff --git a/History.md b/History.md new file mode 100644 index 0000000..aeb6c1b --- /dev/null +++ b/History.md @@ -0,0 +1,21 @@ +## 0.6 / 2017-02-06 + +* Hello, minitar-cli. This is a new gem containing code originally from + archive-tar-minitar. + +* Enhancements: + + * Extracted `bin/minitar` from [minitar][]. + * Replaced Satoru Takabayashi’s [Ruby Progress Bar][] with + [busyloop/powerbar][]. + +* Bugs: + +* Development: + + * Modernized minitar tooling around Hoe. + * Added travis and coveralls. + +[minitar]: https://github.com/halostatue/minitar +[Ruby Progress Bar]: https://namazu.org/~satoru/ruby-progressbar/ +[busyloop/powerbar]: https://github.com/busyloop/powerbar diff --git a/History.rdoc b/History.rdoc deleted file mode 100644 index 1c148f4..0000000 --- a/History.rdoc +++ /dev/null @@ -1,17 +0,0 @@ -== 0.6 / YYYY-MM-DD - -* Hello, minitar-cli. This is a new gem containing code originally from - archive-tar-minitar. - -* Enhancements: - * Extracted bin/minitar from - {minitar}[https://github.com/halostatue/minitar]. - * Replaced Satoru Takabayashi’s - {Ruby Progress Bar}[namazu.org/~satoru/ruby-progressbar/] with - {busyloop/powerbar}[https://github.com/busyloop/powerbar]. - -* Bugs: - -* Development: - * Modernized minitar tooling around Hoe. - * Added travis and coveralls. diff --git a/Licence.rdoc b/Licence.md similarity index 60% rename from Licence.rdoc rename to Licence.md index 22ef108..d26ff21 100644 --- a/Licence.rdoc +++ b/Licence.md @@ -1,12 +1,15 @@ -== Licence +## Licence minitar-cli is free software that may be redistributed and/or modified under the terms of Ruby’s licence or the Simplified BSD licence. -* Copyright 2004–2014 Austin Ziegler. +* Copyright 2004–2017 Austin Ziegler. +* Portions copyright 2004 Mauricio Julio Fernández Pradier. + +### Simplified BSD Licence -=== Simplified BSD Licence See the file docs/bsdl.txt in the main distribution. -=== Ruby’s Licence +### Ruby’s Licence + See the file docs/ruby.txt in the main distribution. diff --git a/Manifest.txt b/Manifest.txt index f124453..017b966 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -1,16 +1,22 @@ -.autotest -.gemtest -.gitignore -.hoerc -Contributing.rdoc -History.rdoc -Licence.rdoc +Contributing.md +History.md +Licence.md Manifest.txt README.rdoc Rakefile bin/minitar docs/bsdl.txt docs/ruby.txt -lib/minitar-cli.rb lib/minitar/cli.rb +lib/minitar/cli/command.rb +lib/minitar/cli/command/create.rb +lib/minitar/cli/command/extract.rb +lib/minitar/cli/command/help.rb +lib/minitar/cli/command/list.rb +lib/minitar/cli/commander.rb +test/fixtures/bad-dir.tar.gz +test/fixtures/spaces.tar.gz test/minitest_helper.rb +test/support/command_line_test_helper.rb +test/test_cli_help.rb +test/test_cli_list.rb diff --git a/README.rdoc b/README.rdoc index 41024a3..9169820 100644 --- a/README.rdoc +++ b/README.rdoc @@ -4,8 +4,9 @@ home :: https://github.com/halostatue/minitar-cli/ code :: https://github.com/halostatue/minitar-cli/ bugs :: https://github.com/halostatue/minitar-cli/issues rdoc :: http://rdoc.info/gems/minitar-cli/ -continuous integration :: {}[https://travis-ci.org/halostatue/minitar-cli] -test coverage :: {Coverage Status}[https://coveralls.io/r/halostatue/minitar-cli] +continuous integration :: {}[https://travis-ci.org/halostatue/minitar-cli] + {}[https://ci.appveyor.com/project/halostatue/minitar-cli] +test coverage :: {Coverage Status}[https://coveralls.io/r/halostatue/minitar-cli] == Description @@ -28,7 +29,3 @@ The minitar-cli tool uses a {Semantic Versioning}[http://semver.org/] scheme with one change: * When PATCH is zero (+0+), it will be omitted from version references. - -:include: Contributing.rdoc - -:include: Licence.rdoc diff --git a/Rakefile b/Rakefile index 84f2c15..1f31f7e 100644 --- a/Rakefile +++ b/Rakefile @@ -13,54 +13,35 @@ Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS'] spec = Hoe.spec 'minitar-cli' do developer('Austin Ziegler', 'halostatue@gmail.com') - self.need_tar = true - self.require_ruby_version '>= 1.8' + require_ruby_version '>= 1.8' - self.history_file = 'History.rdoc' + self.history_file = 'History.md' self.readme_file = 'README.rdoc' - self.extra_rdoc_files = FileList["*.rdoc"].to_a - self.licenses = ["Ruby", "BSD-2-Clause"] - - self.extra_deps << ['archive-tar-minitar', '~> 0.6.0'] - self.extra_deps << ['powerbar', '~> 1.0'] - - self.extra_dev_deps << ['hoe-doofus', '~> 1.0'] - self.extra_dev_deps << ['hoe-gemspec2', '~> 1.1'] - self.extra_dev_deps << ['hoe-git', '~> 1.6'] - self.extra_dev_deps << ['hoe-rubygems', '~> 1.0'] - self.extra_dev_deps << ['hoe-travis', '~> 1.2'] - self.extra_dev_deps << ['minitest', '~> 5.3'] - self.extra_dev_deps << ['minitest-autotest', ['>= 1.0.b', '<2']] - self.extra_dev_deps << ['rake', '~> 10.0'] - self.extra_dev_deps << ['simplecov', '~> 0.7'] - self.extra_dev_deps << ['coveralls', '~> 0.7'] + self.licenses = ['Ruby', 'BSD-2-Clause'] + + extra_deps << ['minitar', '~> 0.6.0'] + extra_deps << ['powerbar', '~> 1.0'] + + extra_dev_deps << ['hoe-doofus', '~> 1.0'] + extra_dev_deps << ['hoe-gemspec2', '~> 1.1'] + extra_dev_deps << ['hoe-git', '~> 1.6'] + extra_dev_deps << ['hoe-rubygems', '~> 1.0'] + extra_dev_deps << ['hoe-travis', '~> 1.2'] + extra_dev_deps << ['minitest', '~> 5.3'] + extra_dev_deps << ['minitest-autotest', ['>= 1.0', '<2']] + extra_dev_deps << ['rake', '>= 10.0', '< 12'] + extra_dev_deps << ['rdoc', '>= 0.0'] end -if RUBY_VERSION >= "1.9" +if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby' namespace :test do - task :coveralls do - spec.test_prelude = [ - 'require "psych"', - 'require "simplecov"', - 'require "coveralls"', - 'SimpleCov.formatter = Coveralls::SimpleCov::Formatter', - 'SimpleCov.start("test_frameworks") { command_name "Minitest" }', - 'gem "minitest"' - ].join('; ') - Rake::Task['test'].execute - end - desc 'Run test coverage' task :coverage do - spec.test_prelude = [ - 'require "simplecov"', - 'SimpleCov.start("test_frameworks") { command_name "Minitest" }', - 'gem "minitest"' - ].join('; ') + spec.test_prelude = 'load ".simplecov-prelude.rb"' Rake::Task['test'].execute end end - Rake::Task['travis'].prerequisites.replace(%w(test:coveralls)) + Rake::Task['travis'].prerequisites.replace(%w(test:coverage)) end diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..2f44052 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +version: '{build}' +skip_tags: true +clone_depth: 10 +init: [] +environment: + matrix: + - ruby_version: 193 + - ruby_version: 200 + - ruby_version: 200-x64 + - ruby_version: 21 + - ruby_version: 21-x64 + - ruby_version: 22 + - ruby_version: 22-x64 + - ruby_version: 23 + - ruby_version: 23-x64 + - ruby_version: 24 + - ruby_version: 24-x64 +install: +- SET PATH=C:\Ruby%ruby_version%\bin;%PATH% +- SET DEV=1 +- ruby --version +- gem --version +- gem install bundler --quiet --no-ri --no-rdoc +- bundle --version +- pushd .. && git clone https://github.com/halostatue/minitar.git && popd +- bundle install +build: off +test_script: + - bundle exec rake test diff --git a/bin/minitar b/bin/minitar index d5a685c..3d5cdbe 100755 --- a/bin/minitar +++ b/bin/minitar @@ -1,6 +1,9 @@ #! /usr/bin/env ruby # coding: utf-8 +git_path = File.expand_path('../../.git', __FILE__) +$:.unshift(File.expand_path('../../lib', __FILE__)) if File.exist?(git_path) + require 'minitar/cli' exit Minitar::CLI.run(ARGV) diff --git a/lib/minitar-cli.rb b/lib/minitar-cli.rb deleted file mode 100644 index 8fd2b7a..0000000 --- a/lib/minitar-cli.rb +++ /dev/null @@ -1,3 +0,0 @@ -# coding: utf-8 - -require 'minitar/cli' diff --git a/lib/minitar/cli.rb b/lib/minitar/cli.rb index 8375bae..c3e9b84 100644 --- a/lib/minitar/cli.rb +++ b/lib/minitar/cli.rb @@ -1,607 +1,64 @@ -#!/usr/bin/env ruby -#-- -# Archive::Tar::Baby 0.5.2 -# Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler -# This is free software with ABSOLUTELY NO WARRANTY. -# -# This program is based on and incorporates parts of RPA::Package from -# rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been -# adapted to be more generic by Austin. -# -# This file contains an adaptation of Ruby/ProgressBar by Satoru -# Takabayashi , copyright 2001 - 2004. -# -# It is licensed under the GNU General Public Licence or Ruby's licence. -# -# $Id$ -#++ +# frozen_string_literal: true -require 'zlib' -require 'optparse' -require 'ostruct' -require 'fileutils' +require 'minitar' -module Minitar - module CLI - VERSION = '0.6' - class CommandPattern - class AbstractCommandError < Exception; end - class UnknownCommandError < RuntimeError; end - class CommandAlreadyExists < RuntimeError; end +# The Minitar command-line application. +class Minitar::CLI + VERSION = '0.6' #:nodoc: - class << self - def add(command) - command = command.new if command.kind_of?(Class) + class AbstractCommandError < Exception; end + class UnknownCommandError < StandardError; end + class CommandAlreadyExists < StandardError; end - @commands ||= {} - if @commands.has_key?(command.name) - raise CommandAlreadyExists - else - @commands[command.name] = command - end - - if command.respond_to?(:altname) - unless @commands.has_key?(command.altname) - @commands[command.altname] = command - end - end - end - alias_method :<<, :add - - attr_reader :default - def default=(command) - if command.kind_of?(CommandPattern) - @default = command - elsif command.kind_of?(Class) - @default = command.new - elsif @commands.has_key?(command) - @default = @commands[command] - else - raise UnknownCommandError - end - end - - def command?(command) - @commands.has_key?(command) - end - - def command(command) - if command?(command) - @commands[command] - else - @default - end - end - - def [](cmd) - self.command(cmd) - end - - def default_ioe(ioe = {}) - ioe[:input] ||= $stdin - ioe[:output] ||= $stdout - ioe[:error] ||= $stderr - ioe - end - end - - def [](args, opts = {}, ioe = {}) - call(args, opts, ioe) - end - - def name - raise AbstractCommandError - end - - def call(args, opts = {}, ioe = {}) - raise AbstractCommandError - end - - def help - raise AbstractCommandError - end - end - - class CommandHelp < CommandPattern - def name - "help" - end - - def call(args, opts = {}, ioe = {}) - ioe = CommandPattern.default_ioe(ioe) - - help_on = args.shift - - if CommandPattern.command?(help_on) - ioe[:output] << CommandPattern[help_on].help - elsif help_on == "commands" - ioe[:output] << <<-EOH -The commands known to minitar are: - - minitar create Creates a new tarfile. - minitar extract Extracts files from a tarfile. - minitar list Lists files in the tarfile. - -All commands accept the options --verbose and --progress, which are -mutually exclusive. In "minitar list", --progress means the same as ---verbose. - - --verbose, -V Performs the requested command verbosely. - --progress, -P Shows a progress bar, if appropriate, for the action - being performed. - - EOH - else - ioe[:output] << "Unknown command: #{help_on}\n" unless help_on.nil? or help_on.empty? - ioe[:output] << self.help - end - - 0 - end - - def help - help = <<-EOH -This is a basic help message containing pointers to more information on -how to use this command-line tool. Try: - - minitar help commands list all 'minitar' commands - minitar help show help on - (e.g., 'minitar help create') - EOH - end - # minitar add Adds a file to an existing tarfile. - # minitar delete Deletes a file from an existing tarfile. - end - - class CommandCreate < CommandPattern - def name - "create" - end - - def altname - "cr" - end - - def call(args, opts = {}, ioe = {}) - argv = [] - - while (arg = args.shift) - case arg - when '--compress', '-z' - opts[:compress] = true - else - argv << arg - end - end - - if argv.size < 2 - ioe[:output] << "Not enough arguments.\n\n" - CommandPattern["help"][["create"]] - return 255 - end - - output = argv.shift - if '-' == output - opts[:name] = "STDOUT" - output = ioe[:output] - opts[:output] = ioe[:error] - else - opts[:name] = output - output = File.open(output, "wb") - opts[:output] = ioe[:output] - end - - if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:compress] - output = Zlib::GzipWriter.new(output) - end - - files = [] - if argv.include?("--") - # Read stdin for the list of files. - files = "" - files << ioe[:input].read while not ioe[:input].eof? - files = files.split(/\r\n|\n|\r/) - args.delete("--") - end - - files << argv.to_a - files.flatten! - - if opts[:verbose] - watcher = lambda do |action, name, stats| - opts[:output] << "#{name}\n" if action == :dir or action == :file_done - end - finisher = lambda { opts[:output] << "\n" } - elsif opts[:progress] - # progress = ProgressBar.new(opts[:name], 1) - watcher = lambda do |action, name, stats| - case action - when :file_start, :dir - # progress.title = File.basename(name) - if action == :dir - # progress.total += 1 - # progress.inc - else - # progress.total += stats[:size] - end - when :file_progress - # progress.inc(stats[:currinc]) - end - end - finisher = lambda do - # progress.title = opts[:name] - # progress.finish - end - else - watcher = nil - finisher = lambda { } - end - - Archive::Tar::Minitar.pack(files, output, &watcher) - finisher.call - 0 - ensure - output.close if output and not output.closed? - end - - def help - help = <<-EOH - minitar create [OPTIONS] + - -Creates a new tarfile. If the tarfile is named .tar.gz or .tgz, then it -will be compressed automatically. If the tarfile is "-", then it will be -output to standard output (stdout) so that minitar may be piped. + def self.run(argv, input = $stdin, output = $stdout, error = $stderr) + new(input, output, error).run(argv) + end -The files or directories that will be packed into the tarfile are -specified after the name of the tarfile itself. Directories will be -processed recursively. If the token "--" is found in the list of files -to be packed, additional filenames will be read from standard input -(stdin). If any file is not found, the packaging will be halted. + attr_reader :commander + attr_reader :ioe + + def initialize(input = $stdin, output = $stdout, error = $stderr) + @ioe = { + :input => input, + :output => output, + :error => error, + } + @commander = Minitar::CLI::Commander.new(ioe) + Minitar::CLI::Command.children.each { |command| + commander.register(command) + } + commander.default_command = 'help' + end -create Options: - --compress, -z Compresses the tarfile with gzip. + def run(argv) + opts = { } - EOH - end + if argv.include?("--version") + output << "minitar #{VERSION}\n" end - class CommandExtract < CommandPattern - def name - "extract" - end - - def altname - "ex" - end - - def call(args, opts = {}, ioe = {}) - argv = [] - output = nil - dest = "." - files = [] - - while (arg = args.shift) - case arg - when '--uncompress', '-z' - opts[:uncompress] = true - when '--pipe' - opts[:output] = ioe[:error] - output = ioe[:output] - when '--output', '-o' - dest = args.shift - else - argv << arg - end - end - - if argv.size < 1 - ioe[:output] << "Not enough arguments.\n\n" - CommandPattern["help"][["extract"]] - return 255 - end - - input = argv.shift - if '-' == input - opts[:name] = "STDIN" - input = ioe[:input] - else - opts[:name] = input - input = File.open(input, "rb") - end - - if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress] - input = Zlib::GzipReader.new(input) - end - - files << argv.to_a - files.flatten! - - if opts[:verbose] - watcher = lambda do |action, name, stats| - opts[:output] << "#{name}\n" if action == :dir or action == :file_done - end - finisher = lambda { opts[:output] << "\n" } - elsif opts[:progress] - # progress = ProgressBar.new(opts[:name], 1) - watcher = lambda do |action, name, stats| - case action - when :file_start, :dir - # progress.title = File.basename(name) - if action == :dir - # progress.total += 1 - # progress.inc - else - # progress.total += stats[:entry].size - end - when :file_progress - # progress.inc(stats[:currinc]) - end - end - finisher = lambda do - # progress.title = opts[:name] - # progress.finish - end - else - watcher = nil - finisher = lambda { } - end - - if output.nil? - Archive::Tar::Minitar.unpack(input, dest, files, &watcher) - finisher.call - else - Archive::Tar::Minitar::Input.open(input) do |inp| - inp.each do |entry| - stats = { - :mode => entry.mode, - :mtime => entry.mtime, - :size => entry.size, - :gid => entry.gid, - :uid => entry.uid, - :current => 0, - :currinc => 0, - :entry => entry - } - - if files.empty? or files.include?(entry.full_name) - if entry.directory? - puts "Directory: #{entry.full_name}" - watcher[:dir, dest, stats] unless watcher.nil? - else - puts "File: #{entry.full_name}" - watcher[:file_start, destfile, stats] unless watcher.nil? - loop do - data = entry.read(4096) - break unless data - stats[:currinc] = output.write(data) - stats[:current] += stats[:currinc] - - watcher[:file_progress, name, stats] unless watcher.nil? - end - watcher[:file_done, name, stats] unless watcher.nil? - end - end - end - end - end - - 0 - end - - def help - help = <<-EOH - minitar extract [OPTIONS] [+] - -Extracts files from an existing tarfile. If the tarfile is named .tar.gz -or .tgz, then it will be uncompressed automatically. If the tarfile is -"-", then it will be read from standard input (stdin) so that minitar -may be piped. - -The files or directories that will be extracted from the tarfile are -specified after the name of the tarfile itself. Directories will be -processed recursively. Files must be specified in full. A file -"foo/bar/baz.txt" cannot simply be specified by specifying "baz.txt". -Any file not found will simply be skipped and an error will be reported. - -extract Options: - --uncompress, -z Uncompresses the tarfile with gzip. - --pipe Emits the extracted files to STDOUT for piping. - --output, -o Extracts the files to the specified directory. - - EOH - end + if argv.include?("--verbose") or argv.include?("-V") + opts[:verbose] = true + argv.delete("--verbose") + argv.delete("-V") end - class CommandList < CommandPattern - def name - "list" - end - - def altname - "ls" - end - - def modestr(mode) - s = "---" - s[0] = ?r if (mode & 4) == 4 - s[1] = ?w if (mode & 2) == 2 - s[2] = ?x if (mode & 1) == 1 - s - end - - def call(args, opts = {}, ioe = {}) - argv = [] - output = nil - dest = "." - files = [] - opts[:field] = "name" - - while (arg = args.shift) - case arg - when '--sort', '-S' - opts[:sort] = true - opts[:field] = args.shift - when '--reverse', '-R' - opts[:reverse] = true - opts[:sort] = true - when '--uncompress', '-z' - opts[:uncompress] = true - when '-l' - opts[:verbose] = true - else - argv << arg - end - end - - if argv.size < 1 - ioe[:output] << "Not enough arguments.\n\n" - CommandPattern["help"][["list"]] - return 255 - end - - input = argv.shift - if '-' == input - opts[:name] = "STDIN" - input = ioe[:input] - else - opts[:name] = input - input = File.open(input, "rb") - end - - if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress] - input = Zlib::GzipReader.new(input) - end - - files << argv.to_a - files.flatten! - - if opts[:verbose] or opts[:progress] - format = "%10s %4d %8s %8s %8d %12s %s" - datefmt = "%b %d %Y" - timefmt = "%b %d %H:%M" - fields = %w(permissions inodes user group size date fullname) - else - format = "%s" - fields = %w(fullname) - end - - opts[:field] = opts[:field].to_sym - opts[:field] = :full_name if opts[:field] == :name - - output = [] - - Archive::Tar::Minitar::Input.open(input) do |inp| - today = Time.now - oneyear = Time.mktime(today.year - 1, today.month, today.day) - inp.each do |entry| - value = format % fields.map do |ff| - case ff - when "permissions" - s = entry.directory? ? "d" : "-" - s << modestr(entry.mode / 0100) - s << modestr(entry.mode / 0010) - s << modestr(entry.mode) - when "inodes" - entry.size / 512 - when "user" - entry.uname || entry.uid || 0 - when "group" - entry.gname || entry.gid || 0 - when "size" - entry.size - when "date" - if Time.at(entry.mtime) > (oneyear) - Time.at(entry.mtime).strftime(timefmt) - else - Time.at(entry.mtime).strftime(datefmt) - end - when "fullname" - entry.full_name - end - end - - if opts[:sort] - output << [entry.send(opts[:field]), value] - else - ioe[:output] << value << "\n" - end - - end - end - - if opts[:sort] - output = output.sort { |a, b| a[0] <=> b[0] } - if opts[:reverse] - output.reverse_each { |oo| ioe[:output] << oo[1] << "\n" } - else - output.each { |oo| ioe[:output] << oo[1] << "\n" } - end - end - - 0 - end - - def help - help = <<-EOH - minitar list [OPTIONS] [+] - -Lists files in an existing tarfile. If the tarfile is named .tar.gz or -.tgz, then it will be uncompressed automatically. If the tarfile is "-", -then it will be read from standard input (stdin) so that minitar may be -piped. - -If --verbose or --progress is specified, then the file list will be -similar to that produced by the Unix command "ls -l". - -list Options: - --uncompress, -z Uncompresses the tarfile with gzip. - --sort [], -S Sorts the list of files by the specified - field. The sort defaults to the filename. - --reverse, -R Reverses the sort. - -l Lists the files in detail. - -Sort Fields: - name, mtime, size - - EOH - end + if argv.include?("--progress") or argv.include?("-P") + opts[:progress] = true + opts[:verbose] = false + argv.delete("--progress") + argv.delete("-P") end - CommandPattern << CommandHelp - CommandPattern << CommandCreate - CommandPattern << CommandExtract - CommandPattern << CommandList - # CommandPattern << CommandAdd - # CommandPattern << CommandDelete - - def self.run(argv, input = $stdin, output = $stdout, error = $stderr) - ioe = { - :input => input, - :output => output, - :error => error, - } - opts = { } - - if argv.include?("--version") - output << "minitar #{VERSION}\n" - end - - if argv.include?("--verbose") or argv.include?("-V") - opts[:verbose] = true - argv.delete("--verbose") - argv.delete("-V") - end - - if argv.include?("--progress") or argv.include?("-P") - opts[:progress] = true - opts[:verbose] = false - argv.delete("--progress") - argv.delete("-P") - end - - command = CommandPattern[(argv.shift or "").downcase] - command ||= CommandPattern["help"] - return command[argv, opts, ioe] - end + command = commander[(argv.shift or "").downcase] + command ||= commander["help"] + return command.call(argv, opts) end end + +require 'minitar/cli/command' +require 'minitar/cli/commander' +require 'minitar/cli/command/help' +require 'minitar/cli/command/create' +require 'minitar/cli/command/extract' +require 'minitar/cli/command/list' diff --git a/lib/minitar/cli/command.rb b/lib/minitar/cli/command.rb new file mode 100644 index 0000000..b73190b --- /dev/null +++ b/lib/minitar/cli/command.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class Minitar::CLI::Command + @children = [] + + attr_reader :commander + attr_reader :ioe + + class << self + attr_reader :children + + def inherited(subclass) + children << subclass + end + end + + module CatchMinitarErrors + def call(args, opts) + run(args, opts) + rescue Archive::Tar::Minitar::Error => error + ioe[:error] << "#{error}\n" + 5 + end + end + + def initialize(commander) + @commander = commander + @ioe = commander.ioe + end + + def name + raise Minitar::CLI::AbstractCommandError + end + + def call(args, opts = {}) + raise Minitar::CLI::AbstractCommandError + end + alias [] call + + def help + raise Minitar::CLI::AbstractCommandError + end +end diff --git a/lib/minitar/cli/command/create.rb b/lib/minitar/cli/command/create.rb new file mode 100644 index 0000000..d3bae79 --- /dev/null +++ b/lib/minitar/cli/command/create.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +class Minitar::CLI::Command::Create < Minitar::CLI::Command + def name + "create" + end + + def altname + "cr" + end + + HELP = <<-EOH + minitar create [OPTIONS] + + +Creates a new tarfile. If the tarfile is named .tar.gz or .tgz, then it +will be compressed automatically. If the tarfile is "-", then it will be +output to standard output (stdout) so that minitar may be piped. + +The files or directories that will be packed into the tarfile are +specified after the name of the tarfile itself. Directories will be +processed recursively. If the token "--" is found in the list of files +to be packed, additional filenames will be read from standard input +(stdin). If any file is not found, the packaging will be halted. + +create Options: + --compress, -z Compresses the tarfile with gzip. + + EOH + + include CatchMinitarErrors + + def run(args, opts = {}) + argv = [] + + while (arg = args.shift) + case arg + when '--compress', '-z' + opts[:compress] = true + else + argv << arg + end + end + + if argv.size < 2 + ioe[:output] << "Not enough arguments.\n\n" + commander.command("help").call(%w(create)) + return 255 + end + + output = argv.shift + if '-' == output + opts[:name] = "STDOUT" + output = ioe[:output] + opts[:output] = ioe[:error] + else + opts[:name] = output + output = File.open(output, "wb") + opts[:output] = ioe[:output] + end + + if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:compress] + output = Zlib::GzipWriter.new(output) + end + + files = [] + if argv.include?("--") + # Read stdin for the list of files. + files = "" + files << ioe[:input].read while not ioe[:input].eof? + files = files.split(/\r\n|\n|\r/) + args.delete("--") + end + + files << argv.to_a + files.flatten! + + if opts[:verbose] + watcher = lambda do |action, name, stats| + opts[:output] << "#{name}\n" if action == :dir or action == :file_done + end + finisher = lambda { opts[:output] << "\n" } + elsif opts[:progress] + require 'powerbar' + progress = PowerBar.new(:msg => opts[:name], :total => 1) + watcher = lambda do |action, name, stats| + progress_info = {} + + case action + when :file_start, :dir + progress_info[:msg] = File.basename(name) + + if action == :dir + progress_info[:total] = progress.total + 1 + progress_info[:done] = progress.done + 1 + else + progress_info[:total] = progress.total + stats[:size] + end + when :file_progress + progress_info[:done] = progress.done + stats[:currinc] + end + + progress.show(progress_info) + end + finisher = lambda do + progress.show(:msg => opts[:name]) + progress.close(true) + end + else + watcher = nil + finisher = lambda { } + end + + Archive::Tar::Minitar.pack(files, output, &watcher) + finisher.call + 0 + ensure + output.close if output and not output.closed? + end + + def help + HELP + end +end diff --git a/lib/minitar/cli/command/extract.rb b/lib/minitar/cli/command/extract.rb new file mode 100644 index 0000000..f7e2444 --- /dev/null +++ b/lib/minitar/cli/command/extract.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +class Minitar::CLI::Command::Extract < Minitar::CLI::Command + def name + "extract" + end + + def altname + "ex" + end + + HELP = <<-EOH + minitar extract [OPTIONS] [+] + +Extracts files from an existing tarfile. If the tarfile is named .tar.gz +or .tgz, then it will be uncompressed automatically. If the tarfile is +"-", then it will be read from standard input (stdin) so that minitar +may be piped. + +The files or directories that will be extracted from the tarfile are +specified after the name of the tarfile itself. Directories will be +processed recursively. Files must be specified in full. A file +"foo/bar/baz.txt" cannot simply be specified by specifying "baz.txt". +Any file not found will simply be skipped and an error will be reported. + +extract Options: + --uncompress, -z Uncompresses the tarfile with gzip. + --pipe Emits the extracted files to STDOUT for piping. + --output, -o Extracts the files to the specified directory. + + EOH + + include CatchMinitarErrors + + def run(args, opts = {}) + argv = [] + output = nil + dest = "." + files = [] + + while (arg = args.shift) + case arg + when '--uncompress', '-z' + opts[:uncompress] = true + when '--pipe' + output = ioe[:output] + ioe[:output] = ioe[:error] + when '--output', '-o' + dest = args.shift + else + argv << arg + end + end + + if argv.size < 1 + ioe[:output] << "Not enough arguments.\n\n" + commander.command("help").call(%w(extract)) + return 255 + end + + input = argv.shift + if '-' == input + opts[:name] = "STDIN" + input = ioe[:input] + else + opts[:name] = input + input = File.open(input, "rb") + end + + if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress] + input = Zlib::GzipReader.new(input) + end + + files << argv.to_a + files.flatten! + + if opts[:verbose] + watcher = lambda do |action, name, stats| + ioe[:output] << "#{name}\n" if action == :dir or action == :file_done + end + finisher = lambda { ioe[:output] << "\n" } + elsif opts[:progress] + require 'powerbar' + progress = PowerBar.new(:msg => opts[:name], :total => 1) + watcher = lambda do |action, name, stats| + progress_info = {} + + case action + when :file_start, :dir + progress_info[:msg] = File.basename(name) + if action == :dir + progress_info[:total] = progress.total + 1 + progress_info[:done] = progress.done + 1 + else + progress_info[:total] = progress.total + stats[:entry].size + end + when :file_progress + progress_info[:done] = progress.done + stats[:currinc] + end + + progress.show(progress_info) + end + finisher = lambda do + progress.show(:msg => opts[:name]) + progress.close(true) + end + else + watcher = nil + finisher = lambda { } + end + + if output.nil? + Archive::Tar::Minitar.unpack(input, dest, files, &watcher) + finisher.call + else + Archive::Tar::Minitar::Input.open(input) do |inp| + inp.each do |entry| + stats = { + :mode => entry.mode, + :mtime => entry.mtime, + :size => entry.size, + :gid => entry.gid, + :uid => entry.uid, + :current => 0, + :currinc => 0, + :entry => entry + } + + if files.empty? or files.include?(entry.full_name) + if entry.directory? + puts "Directory: #{entry.full_name}" + watcher[:dir, dest, stats] unless watcher.nil? + else + puts "File: #{entry.full_name}" + watcher[:file_start, destfile, stats] unless watcher.nil? + loop do + data = entry.read(4096) + break unless data + stats[:currinc] = output.write(data) + stats[:current] += stats[:currinc] + + watcher[:file_progress, name, stats] unless watcher.nil? + end + watcher[:file_done, name, stats] unless watcher.nil? + end + end + end + end + end + + 0 + end + + def help + HELP + end +end diff --git a/lib/minitar/cli/command/help.rb b/lib/minitar/cli/command/help.rb new file mode 100644 index 0000000..a8f7416 --- /dev/null +++ b/lib/minitar/cli/command/help.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class Minitar::CLI::Command::Help < Minitar::CLI::Command + def name + "help" + end + + COMMANDS = <<-EOS +The commands known to minitar are: + + minitar create Creates a new tarfile. + minitar extract Extracts files from a tarfile. + minitar list Lists files in the tarfile. + +All commands accept the options --verbose and --progress, which are +mutually exclusive. In "minitar list", --progress means the same as +--verbose. + + --verbose, -V Performs the requested command verbosely. + --progress, -P Shows a progress bar, if appropriate, for the action + being performed. + + EOS + + BASIC = <<-EOS +This is a basic help message containing pointers to more information on +how to use this command-line tool. Try: + + minitar help commands list all 'minitar' commands + minitar help show help on + (e.g., 'minitar help create') +EOS + + def call(args, opts = {}) + help_on = args.shift + + if commander.command?(help_on) + ioe[:output] << commander[help_on].help + elsif help_on == "commands" + ioe[:output] << COMMANDS + else + ioe[:output] << "Unknown command: #{help_on}\n" unless help_on.nil? or help_on.empty? + ioe[:output] << help + end + + 0 + end + + def help + BASIC + end +end diff --git a/lib/minitar/cli/command/list.rb b/lib/minitar/cli/command/list.rb new file mode 100644 index 0000000..326fb51 --- /dev/null +++ b/lib/minitar/cli/command/list.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +class Minitar::CLI::Command::List < Minitar::CLI::Command + def name + "list" + end + + def altname + "ls" + end + + HELP = <<-EOH + minitar list [OPTIONS] [+] + +Lists files in an existing tarfile. If the tarfile is named .tar.gz or +.tgz, then it will be uncompressed automatically. If the tarfile is "-", +then it will be read from standard input (stdin) so that minitar may be +piped. + +If --verbose or --progress is specified, then the file list will be +similar to that produced by the Unix command "ls -l". + +list Options: + --uncompress, -z Uncompresses the tarfile with gzip. + --sort [], -S Sorts the list of files by the specified + field. The sort defaults to the filename. + --reverse, -R Reverses the sort. + -l Lists the files in detail. + +Sort Fields: + name, mtime, size + + EOH + + def modestr(mode) + s = String.new("---") + s[0] = ?r if (mode & 4) == 4 + s[1] = ?w if (mode & 2) == 2 + s[2] = ?x if (mode & 1) == 1 + s + end + + include CatchMinitarErrors + + def run(args, opts = {}) + argv = [] + output = nil + files = [] + opts[:field] = "name" + + while (arg = args.shift) + case arg + when '--sort', '-S' + opts[:sort] = true + opts[:field] = args.shift + when '--reverse', '-R' + opts[:reverse] = true + opts[:sort] = true + when '--uncompress', '-z' + opts[:uncompress] = true + when '-l' + opts[:verbose] = true + else + argv << arg + end + end + + if argv.size < 1 + ioe[:output] << "Not enough arguments.\n\n" + commander.command("help").call(%w(list)) + return 255 + end + + input = argv.shift + if '-' == input + opts[:name] = "STDIN" + input = ioe[:input] + else + opts[:name] = input + input = File.open(input, "rb") + end + + if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress] + input = Zlib::GzipReader.new(input) + end + + files << argv.to_a + files.flatten! + + if opts[:verbose] or opts[:progress] + format = "%10s %4d %8s %8s %8d %12s %s" + datefmt = "%b %d %Y" + timefmt = "%b %d %H:%M" + fields = %w(permissions inodes user group size date fullname) + else + format = "%s" + fields = %w(fullname) + end + + opts[:field] = opts[:field].to_sym + opts[:field] = :full_name if opts[:field] == :name + + output = [] + + Archive::Tar::Minitar::Input.open(input) do |inp| + today = Time.now + oneyear = Time.mktime(today.year - 1, today.month, today.day) + inp.each do |entry| + value = format % fields.map do |ff| + case ff + when "permissions" + s = String.new(entry.directory? ? "d" : "-") + s << modestr(entry.mode / 0100) + s << modestr(entry.mode / 0010) + s << modestr(entry.mode) + when "inodes" + entry.size / 512 + when "user" + entry.uname || entry.uid || 0 + when "group" + entry.gname || entry.gid || 0 + when "size" + entry.size + when "date" + if Time.at(entry.mtime) > (oneyear) + Time.at(entry.mtime).strftime(timefmt) + else + Time.at(entry.mtime).strftime(datefmt) + end + when "fullname" + entry.full_name + end + end + + if opts[:sort] + output << [entry.send(opts[:field]), value] + else + ioe[:output] << value << "\n" + end + + end + end + + if opts[:sort] + output = output.sort { |a, b| a[0] <=> b[0] } + if opts[:reverse] + output.reverse_each { |oo| ioe[:output] << oo[1] << "\n" } + else + output.each { |oo| ioe[:output] << oo[1] << "\n" } + end + end + + 0 + end + + def help + HELP + end +end diff --git a/lib/minitar/cli/commander.rb b/lib/minitar/cli/commander.rb new file mode 100644 index 0000000..4431d29 --- /dev/null +++ b/lib/minitar/cli/commander.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class Minitar::CLI::Commander + attr_reader :commands + attr_reader :default_command + attr_reader :ioe + + def initialize(ioe) + @ioe = default_ioe(ioe) + @commands = {} + @default_command = nil + end + + def register(command) + command = command.new(self) if command.kind_of?(Class) + raise CommandAlreadyExists if command.commander != self + raise CommandAlreadyExists if command?(command.name) + + commands[command.name] = command + + if command.respond_to?(:altname) + commands[command.altname] = command unless command?(command.altname) + end + end + + def default_command=(command) + command = command.new if command.kind_of?(Class) + + @default_command = + if command.kind_of?(Minitar::CLI::Command) + register(command) unless command?(command.name) + command + elsif command?(command) + commands[command] + else + raise UnknownCommandError + end + end + + def command?(command) + commands.has_key?(command) + end + + def command(command) + if command?(command) + commands[command] + else + default_command + end + end + alias [] command + + def default_ioe(ioe = {}) + ioe[:input] ||= $stdin + ioe[:output] ||= $stdout + ioe[:error] ||= $stderr + ioe + end +end diff --git a/minitar-cli.gemspec b/minitar-cli.gemspec index 1f244c2..0348b2b 100644 --- a/minitar-cli.gemspec +++ b/minitar-cli.gemspec @@ -8,67 +8,61 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Austin Ziegler"] - s.date = "2014-12-22" + s.date = "2017-02-07" s.description = "minitar-cli is a pure-Ruby command-line tool that uses\n{minitar}[https://github.com/halostatue/minitar] to provide a command-line\ntool, +minitar+, for working with POSIX tar(1) archive files.\n\nThis is release 0.6, extracted from {minitar}[https://halostatue.ca/minitar],\nwith modernizations." s.email = ["halostatue@gmail.com"] s.executables = ["minitar"] - s.extra_rdoc_files = ["Contributing.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "docs/bsdl.txt", "docs/ruby.txt", "Contributing.rdoc", "History.rdoc", "Licence.rdoc", "README.rdoc"] - s.files = [".autotest", ".gemtest", ".gitignore", ".hoerc", "Contributing.rdoc", "History.rdoc", "Licence.rdoc", "Manifest.txt", "README.rdoc", "Rakefile", "bin/minitar", "docs/bsdl.txt", "docs/ruby.txt", "lib/minitar-cli.rb", "lib/minitar/cli.rb", "test/minitest_helper.rb"] + s.extra_rdoc_files = ["Contributing.md", "History.md", "Licence.md", "Manifest.txt", "README.rdoc", "docs/bsdl.txt", "docs/ruby.txt"] + s.files = ["Contributing.md", "History.md", "Licence.md", "Manifest.txt", "README.rdoc", "Rakefile", "bin/minitar", "docs/bsdl.txt", "docs/ruby.txt", "lib/minitar/cli.rb", "lib/minitar/cli/command.rb", "lib/minitar/cli/command/create.rb", "lib/minitar/cli/command/extract.rb", "lib/minitar/cli/command/help.rb", "lib/minitar/cli/command/list.rb", "lib/minitar/cli/commander.rb", "test/fixtures/bad-dir.tar.gz", "test/fixtures/spaces.tar.gz", "test/minitest_helper.rb", "test/support/command_line_test_helper.rb", "test/test_cli_help.rb", "test/test_cli_list.rb"] s.homepage = "https://github.com/halostatue/minitar-cli/" s.licenses = ["Ruby", "BSD-2-Clause"] s.rdoc_options = ["--main", "README.rdoc"] s.required_ruby_version = Gem::Requirement.new(">= 1.8") - s.rubygems_version = "2.4.2" + s.rubygems_version = "2.5.1" s.summary = "minitar-cli is a pure-Ruby command-line tool that uses {minitar}[https://github.com/halostatue/minitar] to provide a command-line tool, +minitar+, for working with POSIX tar(1) archive files" if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["~> 0.6.0"]) + s.add_runtime_dependency(%q, ["~> 0.6.0"]) s.add_runtime_dependency(%q, ["~> 1.0"]) - s.add_development_dependency(%q, ["~> 5.4"]) - s.add_development_dependency(%q, ["~> 4.0"]) + s.add_development_dependency(%q, ["~> 5.9"]) s.add_development_dependency(%q, ["~> 1.0"]) s.add_development_dependency(%q, ["~> 1.1"]) s.add_development_dependency(%q, ["~> 1.6"]) s.add_development_dependency(%q, ["~> 1.0"]) s.add_development_dependency(%q, ["~> 1.2"]) - s.add_development_dependency(%q, ["< 2", ">= 1.0.b"]) - s.add_development_dependency(%q, ["~> 10.0"]) - s.add_development_dependency(%q, ["~> 0.7"]) - s.add_development_dependency(%q, ["~> 0.7"]) - s.add_development_dependency(%q, ["~> 3.13"]) + s.add_development_dependency(%q, ["< 2", ">= 1.0"]) + s.add_development_dependency(%q, ["< 12", ">= 10.0"]) + s.add_development_dependency(%q, [">= 0.0"]) + s.add_development_dependency(%q, ["~> 3.16"]) else - s.add_dependency(%q, ["~> 0.6.0"]) + s.add_dependency(%q, ["~> 0.6.0"]) s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 5.4"]) - s.add_dependency(%q, ["~> 4.0"]) + s.add_dependency(%q, ["~> 5.9"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.1"]) s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.2"]) - s.add_dependency(%q, ["< 2", ">= 1.0.b"]) - s.add_dependency(%q, ["~> 10.0"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 3.13"]) + s.add_dependency(%q, ["< 2", ">= 1.0"]) + s.add_dependency(%q, ["< 12", ">= 10.0"]) + s.add_dependency(%q, [">= 0.0"]) + s.add_dependency(%q, ["~> 3.16"]) end else - s.add_dependency(%q, ["~> 0.6.0"]) + s.add_dependency(%q, ["~> 0.6.0"]) s.add_dependency(%q, ["~> 1.0"]) - s.add_dependency(%q, ["~> 5.4"]) - s.add_dependency(%q, ["~> 4.0"]) + s.add_dependency(%q, ["~> 5.9"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.1"]) s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.2"]) - s.add_dependency(%q, ["< 2", ">= 1.0.b"]) - s.add_dependency(%q, ["~> 10.0"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 0.7"]) - s.add_dependency(%q, ["~> 3.13"]) + s.add_dependency(%q, ["< 2", ">= 1.0"]) + s.add_dependency(%q, ["< 12", ">= 10.0"]) + s.add_dependency(%q, [">= 0.0"]) + s.add_dependency(%q, ["~> 3.16"]) end end diff --git a/test/fixtures/bad-dir.tar.gz b/test/fixtures/bad-dir.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..955d026a4996e220e38f0470923b0805e72d7da1 GIT binary patch literal 119 zcmb2|=HO`3(2QVWPD)JCP01|MD@iP3xP1AtzK*}H*2vh9B8Tws87J zY3&LNPm-CJG)WAzR~|&dHUY~7eqQ<-kYKPUp!We-TgnG>uuk5j{)A{ zDgTGR$K}5*vV!tI2I+YAk^X(&yCMF|_Mz(LO|wlNS51g__~Ks~JC1+rX#Yh)>|M7Z z{_m?*(C(i<{?$SMZ$SLVuuuQGukY7wSA}|qxBaK{ar!%#k^V8nX~msL`p39JL;Qbk ztF8?B4qyBig&xQM@Br~2gLKG4$V>VU@xR$MO;zzq~NC|Duo%&;X==(u*?0f8Wg?>R^k1X8!fxx;W_nDb4(U z6w<+pt0tZOuU?YQH=Bp*vF*gh|8%Ct?LV72Mfo3tbOC^?CmjM;ElC&kI`AG~b?W|K zomu^~|2MPy{Xgvg4KGPQw9D$Yy}SL;u9v&APgcvR-{GnMU1r>P{AYNO`acHg!XE$t z005pz_m1AbegA*mclBa7^nULHbpB`F{=Y2_$N!(sf202&o^*r*007{Zy{Y||q|3J4 zhMfRDasF4i+-RM<{rs=foc|m}lKxSx?&>Dk;ZHOF`Y&b2=l>b)nE#DJy70pJlg^_E z=X?2|G;R4M%sV{ge^%&&^Ivtw`9D!emn{H*w|hhFoS|97ze(>Tt5 zk3%}o;-X1sU%XM0Hud6+_WvmTy#GhD|0jwh{i`mkby>;P|H_%{;Ql|y`kyGID-awL zC;gN3(|lduRog!4zXrMk#uxvE8sGn6GnEtnF-YffOyCXapQNjLT@N8}TQ>FnDyZ$I zn))&jPy8279qj+L-2W4Ybk+p`0002sddT(vll1TTmn!TA0N?sg6UG%OW?ire?Ff7HMuIN|6`C2MgWLHy0AS%&+C6*)_??B z2YmYaUsrtF|EXC28^w?5Up94sJaBsY7rC{?)A@h<`@c#5@D9>{>dQZ8{`=omez5+F z^`DVQSIGc?OCViaVQPo%|FT`J=IiBz{eY9(|5