// SharpZipLibrary samples // Copyright (c) 2007, AlphaSierraPapa // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this list // of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // // - Neither the name of the SharpDevelop team nor the names of its contributors may be used to // endorse or promote products derived from this software without specific prior written // permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &AS IS& AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.IO; using ICSharpCode.SharpZipLib; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.BZip2; using ICSharpCode.SharpZipLib.Tar; /// /// The tar class implements a simplistic version of the /// traditional UNIX tar command. It currently supports /// creating, listing, and extracting from archives. /// It supports GZIP, unix compress and bzip2 compression /// GNU long filename extensions are supported, POSIX extensions are not yet supported... /// See the help (-? or --help) for option details. /// public class Tar { /// /// The compresion to use when creating archives. /// enum Compression { None, Compress, Gzip, Bzip2 } /// /// Operation to perform on archive /// enum Operation { List, Create, Extract } #region Instance Fields /// /// Flag that determines if verbose feedback is to be provided. /// bool verbose; /// /// What kind of to use. /// Compression compression = Compression.None; /// /// The to perform. /// Operation operation = Operation.List; /// /// True if we are not to overwrite existing files. (Unix noKlobber option) /// bool keepOldFiles; /// /// True if we are to convert ASCII text files from local line endings /// to the UNIX standard '\n'. /// bool asciiTranslate; /// /// The archive name provided on the command line, '-' if stdio. /// string archiveName; /// /// The blocking factor to use for the tar archive IO. Set by the '-b' option. /// int blockingFactor; /// /// The userId to use for files written to archives. Set by '-U' option. /// int userId; /// /// The userName to use for files written to archives. Set by '-u' option. /// string userName; /// /// The groupId to use for files written to archives. Set by '-G' option. /// int groupId; /// /// The groupName to use for files written to archives. Set by '-g' option. /// string groupName; #endregion /// /// Initialise default instance of . /// Sets up the default userName with the system 'UserName' property. /// public Tar() { this.blockingFactor = TarBuffer.DefaultBlockFactor; this.userId = 0; string sysUserName = Environment.UserName; this.userName = ((sysUserName == null) ? "" : sysUserName); this.groupId = 0; this.groupName = "None"; } /// /// The main entry point of the tar class. /// public static void Main(string[] argv) { Tar tarApp = new Tar(); tarApp.InstanceMain(argv); } /// /// This is the "real" main. The class main() instantiates a tar object /// for the application and then calls this method. Process the arguments /// and perform the requested operation. /// public void InstanceMain(string[] argv) { TarArchive archive = null; int argIdx = this.ProcessArguments(argv); if (this.archiveName != null && ! this.archiveName.Equals("-")) { if (operation == Operation.Create) { string dirName = Path.GetDirectoryName(archiveName); if ((dirName.Length > 0) && !Directory.Exists(dirName)) { Console.Error.WriteLine("Directory for archive doesnt exist"); return; } } else { if (File.Exists(this.archiveName) == false) { Console.Error.WriteLine("File does not exist " + this.archiveName); return; } } } if (operation == Operation.Create) { // WRITING Stream outStream = Console.OpenStandardOutput(); if (this.archiveName != null && ! this.archiveName.Equals("-")) { outStream = File.Create(archiveName); } if (outStream != null) { switch (this.compression) { case Compression.Compress: outStream = new DeflaterOutputStream(outStream); break; case Compression.Gzip: outStream = new GZipOutputStream(outStream); break; case Compression.Bzip2: outStream = new BZip2OutputStream(outStream, 9); break; } archive = TarArchive.CreateOutputTarArchive(outStream, this.blockingFactor); } } else { // EXTRACTING OR LISTING Stream inStream = Console.OpenStandardInput(); if (this.archiveName != null && ! this.archiveName.Equals( "-" )) { inStream = File.OpenRead(archiveName); } if (inStream != null) { switch (this.compression) { case Compression.Compress: inStream = new InflaterInputStream(inStream); break; case Compression.Gzip: inStream = new GZipInputStream(inStream); break; case Compression.Bzip2: inStream = new BZip2InputStream(inStream); break; } archive = TarArchive.CreateInputTarArchive(inStream, this.blockingFactor); } } if (archive != null) { // SET ARCHIVE OPTIONS archive.SetKeepOldFiles(this.keepOldFiles); archive.AsciiTranslate = this.asciiTranslate; archive.SetUserInfo(this.userId, this.userName, this.groupId, this.groupName); } if (archive == null) { Console.Error.WriteLine( "no processing due to errors" ); } else if (operation == Operation.Create) { // WRITING if (verbose) { archive.ProgressMessageEvent += new ProgressMessageHandler(ShowTarProgressMessage); } for ( ; argIdx < argv.Length ; ++argIdx ) { string[] fileNames = GetFilesForSpec(argv[argIdx]); if (fileNames.Length > 0) { foreach (string name in fileNames) { TarEntry entry = TarEntry.CreateEntryFromFile(name); archive.WriteEntry(entry, true); } } else { Console.Error.Write("No files for " + argv[argIdx]); } } } else if (operation == Operation.List) { // LISTING archive.ProgressMessageEvent += new ProgressMessageHandler(ShowTarProgressMessage); archive.ListContents(); } else { // EXTRACTING string userDir = Environment.CurrentDirectory; if (verbose) { archive.ProgressMessageEvent += new ProgressMessageHandler(ShowTarProgressMessage); } if (userDir != null) { archive.ExtractContents(userDir); } } if (archive != null) { // CLOSE ARCHIVE archive.Close(); } } /// /// Display progress information on console /// public void ShowTarProgressMessage(TarArchive archive, TarEntry entry, string message) { if (entry.TarHeader.TypeFlag != TarHeader.LF_NORMAL && entry.TarHeader.TypeFlag != TarHeader.LF_OLDNORM) { Console.WriteLine("Entry type " + (char)entry.TarHeader.TypeFlag + " found!"); } if (message != null) Console.Write(entry.Name + " " + message); else { if (this.verbose) { string modeString = DecodeType(entry.TarHeader.TypeFlag, entry.Name.EndsWith("/")) + DecodeMode(entry.TarHeader.Mode); string userString = (entry.UserName == null || entry.UserName.Length == 0) ? entry.UserId.ToString() : entry.UserName; string groupString = (entry.GroupName == null || entry.GroupName.Length == 0) ? entry.GroupId.ToString() : entry.GroupName; Console.WriteLine(string.Format("{0} {1}/{2} {3,8} {4:yyyy-MM-dd HH:mm:ss} {5}", modeString, userString, groupString, entry.Size, entry.ModTime.ToLocalTime(), entry.Name)); } else { Console.WriteLine(entry.Name); } } } /// /// /// Process arguments, handling options, and return the index of the /// first non-option argument. /// /// /// The index of the first non-option argument. /// int ProcessArguments(string[] args) { int idx = 0; bool bailOut = false; bool gotOP = false; for ( ; idx < args.Length ; ++idx ) { string arg = args[ idx ]; if (!arg.StartsWith("-")) { break; } if (arg.StartsWith("--" )) { int valuePos = arg.IndexOf('='); string argValue = null; if (valuePos >= 0) { argValue = arg.Substring(valuePos + 1); arg = arg.Substring(0, valuePos); } if (arg.Equals( "--help")) { ShowHelp(); Environment.Exit(1); } else if (arg.Equals( "--version")) { Version(); Environment.Exit(1); } else if (arg.Equals("--extract")) { gotOP = true; operation = Operation.Extract; } else if (arg.Equals("--list")) { gotOP = true; operation = Operation.List; } else if (arg.Equals("--create")) { gotOP = true; operation = Operation.Create; } else if (arg.Equals("--gzip")) { compression = Compression.Gzip; } else if (arg.Equals("--bzip2")) { compression = Compression.Bzip2; } else if (arg.Equals("--compress")) { compression = Compression.Compress; } else if (arg.Equals("--blocking-factor")) { if (argValue == null || argValue.Length == 0) Console.Error.WriteLine("expected numeric blocking factor"); else { try { this.blockingFactor = Int32.Parse(argValue); if ( blockingFactor <= 0 ) { Console.Error.WriteLine("Blocking factor {0} is invalid", blockingFactor); bailOut = true; } } catch { Console.Error.WriteLine("invalid blocking factor"); } } } else if (arg.Equals("--verbose")) { verbose = true; } else if (arg.Equals("--keep-old-files")) { keepOldFiles = true; } else if (arg.Equals("--record-size")) { if (argValue == null || argValue.Length == 0) { Console.Error.WriteLine("expected numeric record size"); bailOut = true; } else { int size; try { size = Int32.Parse(argValue); if (size % TarBuffer.BlockSize != 0) { Console.Error.WriteLine("Record size must be a multiple of " + TarBuffer.BlockSize.ToString()); bailOut = true; } else blockingFactor = size / TarBuffer.BlockSize; } catch { Console.Error.WriteLine("non-numeric record size"); bailOut = true; } } } else { Console.Error.WriteLine("unknown option: " + arg); ShowHelp(); Environment.Exit(1); } } else { for (int cIdx = 1; cIdx < arg.Length; ++cIdx) { switch (arg[cIdx]) { case '?': ShowHelp(); Environment.Exit(1); break; case 'f': this.archiveName = args[++idx]; break; case 'j': compression = Compression.Bzip2; break; case 'z': compression = Compression.Gzip; break; case 'Z': compression = Compression.Compress; break; case 'e': asciiTranslate = true; break; case 'c': gotOP = true; operation = Operation.Create; break; case 'x': gotOP = true; operation = Operation.Extract; break; case 't': gotOP = true; operation = Operation.List; break; case 'k': keepOldFiles = true; break; case 'b': blockingFactor = Int32.Parse(args[++idx]); break; case 'u': userName = args[++idx]; break; case 'U': userId = Int32.Parse(args[ ++idx ]); break; case 'g': groupName = args[++idx]; break; case 'G': groupId = Int32.Parse(args[ ++idx ]); break; case 'v': verbose = true; break; default: Console.Error.WriteLine("unknown option: " + arg[cIdx]); ShowHelp(); Environment.Exit(1); break; } } } } if (!gotOP) { Console.Error.WriteLine("you must specify an operation option (c, x, or t)"); Console.Error.WriteLine("Try tar --help"); bailOut = true; } if (bailOut == true) { Environment.Exit(1); } return idx; } static string[] GetFilesForSpec(string spec) { string dir = Path.GetDirectoryName(spec); if (dir == null || dir.Length == 0) dir = Directory.GetCurrentDirectory(); return System.IO.Directory.GetFiles(dir, Path.GetFileName(spec)); } static string DecodeType(int type, bool slashTerminated) { string result = "?"; switch (type) { case TarHeader.LF_OLDNORM: // -jr- TODO this decoding is incomplete, not all possible known values are decoded... case TarHeader.LF_NORMAL: case TarHeader.LF_LINK: if (slashTerminated) result = "d"; else result = "-"; break; case TarHeader.LF_DIR: result = "d"; break; case TarHeader.LF_GNU_VOLHDR: result = "V"; break; case TarHeader.LF_GNU_MULTIVOL: result = "M"; break; case TarHeader.LF_CONTIG: result = "C"; break; case TarHeader.LF_FIFO: result = "p"; break; case TarHeader.LF_SYMLINK: result = "l"; break; case TarHeader.LF_CHR: result = "c"; break; case TarHeader.LF_BLK: result = "b"; break; } return result; } static string DecodeMode(int mode) { const int S_ISUID = 0x0800; const int S_ISGID = 0x0400; const int S_ISVTX = 0x0200; const int S_IRUSR = 0x0100; const int S_IWUSR = 0x0080; const int S_IXUSR = 0x0040; const int S_IRGRP = 0x0020; const int S_IWGRP = 0x0010; const int S_IXGRP = 0x0008; const int S_IROTH = 0x0004; const int S_IWOTH = 0x0002; const int S_IXOTH = 0x0001; System.Text.StringBuilder result = new System.Text.StringBuilder(); result.Append((mode & S_IRUSR) != 0 ? 'r' : '-'); result.Append((mode & S_IWUSR) != 0 ? 'w' : '-'); result.Append((mode & S_ISUID) != 0 ? ((mode & S_IXUSR) != 0 ? 's' : 'S') : ((mode & S_IXUSR) != 0 ? 'x' : '-')); result.Append((mode & S_IRGRP) != 0 ? 'r' : '-'); result.Append((mode & S_IWGRP) != 0 ? 'w' : '-'); result.Append((mode & S_ISGID) != 0 ? ((mode & S_IXGRP) != 0 ? 's' : 'S') : ((mode & S_IXGRP) != 0 ? 'x' : '-')); result.Append((mode & S_IROTH) != 0 ? 'r' : '-'); result.Append((mode & S_IWOTH) != 0 ? 'w' : '-'); result.Append( (mode & S_ISVTX) != 0 ? ((mode & S_IXOTH) != 0 ? 't' : 'T') : ((mode & S_IXOTH) != 0 ? 'x' : '-')); return result.ToString(); } static string SharpZipVersion() { System.Reflection.Assembly zipAssembly = System.Reflection.Assembly.GetAssembly(new TarHeader().GetType()); Version v = zipAssembly.GetName().Version; return "#ZipLib v" + v.Major + "." + v.Minor + "." + v.Build + "." + v.Revision; } /// /// Print version information. /// static void Version() { Console.Error.WriteLine( "tar 2.0.6.2" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "{0}", SharpZipVersion() ); Console.Error.WriteLine( "Copyright (c) 2002 by Mike Krueger" ); Console.Error.WriteLine( "Copyright (c) 1998,1999 by Tim Endres (Java version)" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "This program is free software licensed to you under the" ); Console.Error.WriteLine( "GNU General Public License. See the accompanying LICENSE" ); Console.Error.WriteLine( "file, or the webpage or," ); Console.Error.WriteLine( "visit www.gnu.org for more details." ); Console.Error.WriteLine( "" ); } /// /// Print help information. /// static private void ShowHelp() { Console.Error.WriteLine( "Usage: tar [option]... [file]..." ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "Examples:" ); Console.Error.WriteLine( " tar -cf archive.tar foo bar # create archive.tar from files foo and bar" ); Console.Error.WriteLine( " tar -tvf archive.tar # List all files in archive tar verbosely" ); Console.Error.WriteLine( " tar -xvf archive.tar # Extract all files from archive.tar" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "Main operation mode:" ); Console.Error.WriteLine( " -t, --list list the contents of an archive" ); Console.Error.WriteLine( " -x, --extract extract files from an archive" ); Console.Error.WriteLine( " -c, --create create a new archive" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "Options:" ); Console.Error.WriteLine( " -f file, use 'file' as the tar archive" ); Console.Error.WriteLine( " -e, Turn on ascii translation" ); Console.Error.WriteLine( " -z, --gzip use gzip compression" ); Console.Error.WriteLine( " -Z, --compress use unix compress" ); Console.Error.WriteLine( " -j, --bzip2 use bzip2 compression" ); Console.Error.WriteLine( " -k, --keep-old-files dont overwrite existing files when extracting" ); Console.Error.WriteLine( " -b blks, set blocking factor (blks * 512 bytes per record)" ); Console.Error.WriteLine( " --record-size=SIZE SIZE bytes per record, multiple of 512"); Console.Error.WriteLine( " -u name, set user name to 'name'" ); Console.Error.WriteLine( " -U id, set user id to 'id'" ); Console.Error.WriteLine( " -g name, set group name to 'name'" ); Console.Error.WriteLine( " -G id, set group id to 'id'" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "Informative output:" ); Console.Error.WriteLine( " -?, --help print this help then exit" ); Console.Error.WriteLine( " --version, print tar program version information" ); Console.Error.WriteLine( " -v, --verbose verbosely list files processed" ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "The translation option -e will translate from local line" ); Console.Error.WriteLine( "endings to UNIX line endings of '\\n' when writing tar" ); Console.Error.WriteLine( "archives, and from UNIX line endings into local line endings" ); Console.Error.WriteLine( "when extracting archives." ); Console.Error.WriteLine( "" ); Console.Error.WriteLine( "This tar defaults to -b " + TarBuffer.DefaultBlockFactor.ToString()); Environment.Exit(1); } } /* ** Authored by Timothy Gerard Endres ** ** ** This work has been placed into the public domain. ** You may use this work in any way and for any purpose you wish. ** ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR ** REDISTRIBUTION OF THIS SOFTWARE. ** */