RTGzip.cpp revision 4f3d37f3c8ea851c3d57304fac430764b77a84dc
/* $Id$ */
/** @file
* IPRT - GZIP Testcase Utility.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/zip.h>
#include <iprt/buildconfig.h>
#include <iprt/file.h>
#include <iprt/getopt.h>
#include <iprt/initterm.h>
#include <iprt/message.h>
#include <iprt/param.h>
#include <iprt/stream.h>
#include <iprt/string.h>
#include <iprt/vfs.h>
#include <iprt/zip.h>
static bool isStdHandleATty(int fd)
{
/** @todo IPRT is missing this */
return false;
}
/**
* Pushes data from the input to the output I/O streams.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
* @param hVfsIn The input I/O stream.
* @param hVfsOut The input I/O stream.
*/
static RTEXITCODE gzipPush(RTVFSIOSTREAM hVfsIn, RTVFSIOSTREAM hVfsOut)
{
for (;;)
{
uint8_t abBuf[_64K];
size_t cbRead;
int rc = RTVfsIoStrmRead(hVfsIn, abBuf, sizeof(abBuf), true /*fBlocking*/, &cbRead);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmRead failed: %Rrc", rc);
if (rc == VINF_EOF && cbRead == 0)
return RTEXITCODE_SUCCESS;
rc = RTVfsIoStrmWrite(hVfsOut, abBuf, cbRead, true /*fBlocking*/, NULL /*cbWritten*/);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmWrite failed: %Rrc", rc);
}
}
static RTEXITCODE gzipCompress(RTVFSIOSTREAM hVfsIn, RTVFSIOSTREAM hVfsOut)
{
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Compression is not yet implemented, sorry.");
}
static RTEXITCODE gzipCompressFile(const char *pszFile, bool fStdOut, bool fForce, PRTVFSIOSTREAM phVfsStdOut)
{
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Compression is not yet implemented, sorry.");
}
static RTEXITCODE gzipDecompress(RTVFSIOSTREAM hVfsIn, RTVFSIOSTREAM hVfsOut)
{
RTEXITCODE rcExit;
RTVFSIOSTREAM hVfsGunzip;
int rc = RTZipGzipDecompressIoStream(hVfsIn, 0 /*fFlags*/, &hVfsGunzip);
if (RT_SUCCESS(rc))
{
rcExit = gzipPush(hVfsGunzip, hVfsOut);
RTVfsIoStrmRelease(hVfsGunzip);
}
else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
return rcExit;
}
/**
* Handles a file on the command line.
*
* @returns exit code.
* @param pszFile The file to handle.
* @param fStdOut Whether to output to standard output or not.
* @param fForce Whether to output to or input from terminals.
* @param phVfsStdOut Pointer to the standard out handle.
* (input/output)
*/
static RTEXITCODE gzipDecompressFile(const char *pszFile, bool fStdOut, bool fForce, PRTVFSIOSTREAM phVfsStdOut)
{
/*
* Open the specified input file.
*/
const char *pszError;
RTVFSIOSTREAM hVfsIn;
int rc = RTVfsChainOpenIoStream(pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &hVfsIn, &pszError);
if (RT_FAILURE(rc))
{
if (pszError && *pszError)
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"RTVfsChainOpenIoStream failed with rc=%Rrc:\n"
" '%s'\n",
" %*s^\n",
rc, pszFile, pszError - pszFile, "");
return RTMsgErrorExit(RTEXITCODE_FAILURE,
"RTVfsChainOpenIoStream failed with rc=%Rrc: '%s'",
rc, pszFile);
}
/*
* Output the output file.
*/
RTVFSIOSTREAM hVfsOut;
char szFinal[RTPATH_MAX];
if (fStdOut)
{
if (*phVfsStdOut == NIL_RTVFSIOSTREAM)
{
rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, 0 /*fOpen*/, true /*fLeaveOpen*/, phVfsStdOut);
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set up standard out: %Rrc", rc);
}
hVfsOut = *phVfsStdOut;
szFinal[0] = '\0';
}
else
{
rc = RTStrCopy(szFinal, sizeof(szFinal), pszFile);
/** @todo remove the extension? Or are we supposed
* to get the org name from the gzip stream? */
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Decompressing to file is not implemented");
}
/*
* Do the decompressing, then flush and close the output stream (unless
* it is stdout).
*/
RTEXITCODE rcExit = gzipDecompress(hVfsIn, hVfsOut);
RTVfsIoStrmRelease(hVfsIn);
rc = RTVfsIoStrmFlush(hVfsOut);
if (RT_FAILURE(rc))
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to flush the output file: %Rrc", rc);
RTVfsIoStrmRelease(hVfsOut);
/*
* Remove the input file, if that's the desire of the caller, or
* remove the output file on decompression failure.
*/
if (!fStdOut)
{
if (rcExit == RTEXITCODE_SUCCESS)
{
rc = RTFileDelete(pszFile);
if (RT_FAILURE(rc))
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileDelete failed with %rc: '%s'", rc, pszFile);
}
else
{
/* should we do this? */
rc = RTFileDelete(szFinal);
if (RT_FAILURE(rc))
RTMsgError("RTFileDelete failed with %rc: '%s'", rc, pszFile);
}
}
return rcExit;
}
static RTEXITCODE gzipTestFile(const char *pszFile, bool fForce)
{
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Testiong has not been implemented");
}
static RTEXITCODE gzipListFile(const char *pszFile, bool fForce)
{
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Listing has not been implemented");
}
int main(int argc, char **argv)
{
int rc = RTR3Init();
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/*
* Parse the command line.
*/
static const RTGETOPTDEF s_aOptions[] =
{
{ "--ascii", 'a', RTGETOPT_REQ_NOTHING },
{ "--stdout", 'c', RTGETOPT_REQ_NOTHING },
{ "--to-stdout", 'c', RTGETOPT_REQ_NOTHING },
{ "--decompress", 'd', RTGETOPT_REQ_NOTHING },
{ "--uncompress", 'd', RTGETOPT_REQ_NOTHING },
{ "--force", 'f', RTGETOPT_REQ_NOTHING },
{ "--list", 'l', RTGETOPT_REQ_NOTHING },
{ "--no-name", 'n', RTGETOPT_REQ_NOTHING },
{ "--name", 'N', RTGETOPT_REQ_NOTHING },
{ "--quiet", 'q', RTGETOPT_REQ_NOTHING },
{ "--recursive", 'r', RTGETOPT_REQ_NOTHING },
{ "--suffix", 'S', RTGETOPT_REQ_STRING },
{ "--test", 't', RTGETOPT_REQ_NOTHING },
{ "--verbose", 'v', RTGETOPT_REQ_NOTHING },
{ "--fast", '1', RTGETOPT_REQ_NOTHING },
{ "-1", '1', RTGETOPT_REQ_NOTHING },
{ "-2", '2', RTGETOPT_REQ_NOTHING },
{ "-3", '3', RTGETOPT_REQ_NOTHING },
{ "-4", '4', RTGETOPT_REQ_NOTHING },
{ "-5", '5', RTGETOPT_REQ_NOTHING },
{ "-6", '6', RTGETOPT_REQ_NOTHING },
{ "-7", '7', RTGETOPT_REQ_NOTHING },
{ "-8", '8', RTGETOPT_REQ_NOTHING },
{ "-9", '9', RTGETOPT_REQ_NOTHING },
{ "--best", '9', RTGETOPT_REQ_NOTHING }
};
bool fAscii = false;
bool fStdOut = false;
bool fDecompress = false;
bool fForce = false;
bool fList = false;
bool fName = true;
bool fQuiet = false;
bool fRecursive = false;
const char *pszSuff = ".gz";
bool fTest = false;
unsigned uLevel = 6;
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
unsigned cProcessed = 0;
RTVFSIOSTREAM hVfsStdOut= NIL_RTVFSIOSTREAM;
RTGETOPTSTATE GetState;
rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
RTGETOPTINIT_FLAGS_OPTS_FIRST);
for (;;)
{
RTGETOPTUNION ValueUnion;
rc = RTGetOpt(&GetState, &ValueUnion);
switch (rc)
{
case 0:
{
/*
* If we've processed any files we're done. Otherwise take
* input from stdin and write the output to stdout.
*/
if (cProcessed > 0)
return rcExit;
#if 0
rc = RTVfsFileFromRTFile(1,
RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
true /*fLeaveOpen*/,
&hVfsOut);
if (!fForce && isStdHandleATty(fDecompress ? 0 : 1))
return RTMsgErrorExit(RTEXITCODE_SYNTAX,
"Yeah, right. I'm not %s any compressed data %s the terminal without --force.\n",
fDecompress ? "reading" : "writing",
fDecompress ? "from" : "to");
#else
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "reading from standard input has not yet been implemented");
#endif
return rcExit;
}
case VINF_GETOPT_NOT_OPTION:
{
if (!*pszSuff && !fStdOut)
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --suffix option specified an empty string");
if (!fStdOut && RTVfsChainIsSpec(ValueUnion.psz))
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must use standard out with VFS chain specifications");
RTEXITCODE rcExit2;
if (fList)
rcExit2 = gzipListFile(ValueUnion.psz, fForce);
else if (fTest)
rcExit2 = gzipTestFile(ValueUnion.psz, fForce);
else if (fDecompress)
rcExit2 = gzipDecompressFile(ValueUnion.psz, fStdOut, fForce, &hVfsStdOut);
else
rcExit2 = gzipCompressFile(ValueUnion.psz, fStdOut, fForce, &hVfsStdOut);
if (rcExit2 != RTEXITCODE_SUCCESS)
rcExit = rcExit2;
cProcessed++;
break;
}
case 'a': fAscii = true; break;
case 'c': fStdOut = true; break;
case 'd': fDecompress = true; break;
case 'f': fForce = true; break;
case 'l': fList = true; break;
case 'n': fName = false; break;
case 'N': fName = true; break;
case 'q': fQuiet = true; break;
case 'r': fRecursive = true; break;
case 'S': pszSuff = ValueUnion.psz; break;
case 't': fTest = true; break;
case 'v': fQuiet = false; break;
case '1': uLevel = 1; break;
case '2': uLevel = 2; break;
case '3': uLevel = 3; break;
case '4': uLevel = 4; break;
case '5': uLevel = 5; break;
case '6': uLevel = 6; break;
case '7': uLevel = 7; break;
case '8': uLevel = 8; break;
case '9': uLevel = 9; break;
case 'h':
RTPrintf("Usage: to be written\nOption dump:\n");
for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
return RTEXITCODE_SUCCESS;
case 'V':
RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
return RTEXITCODE_SUCCESS;
default:
return RTGetOptPrintError(rc, &ValueUnion);
}
}
}