/** * Opens the output file. * * @returns Command exit, error messages written using RTMsg*. * * @param pszFile The input filename. * @param pOpts The options, szOutput will be filled in by this * function on success. * @param phVfsIos Where to return the output stream handle. * * @remarks This is actually not quite the way we need to do things. * * First of all, we need a GZIP file system stream for a real GZIP * implementation, since there may be more than one file in the gzipped * file. * * Second, we need to open the output files as we encounter files in the input * file system stream. The gzip format contains timestamp and usually a * filename, the default is to use this name (see the --no-name * option). */ static RTEXITCODE gzipOpenOutput(const char *pszFile, PRTGZIPCMDOPTS pOpts, PRTVFSIOSTREAM phVfsIos) { int rc; if (!strcmp(pszFile, "-") || pOpts->fStdOut) { strcpy(pOpts->szOutput, "-"); if ( !pOpts->fForce && !pOpts->fDecompress && gzipIsStdHandleATty(RTHANDLESTD_OUTPUT)) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Yeah, right. I'm not writing any compressed data to the terminal without --force.\n"); rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, true /*fLeaveOpen*/, phVfsIos); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening standard output: %Rrc", rc); } else { Assert(!RTVfsChainIsSpec(pszFile)); /* Construct an output filename. */ rc = RTStrCopy(pOpts->szOutput, sizeof(pOpts->szOutput), pszFile); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: %Rrc", rc); if (pOpts->fDecompress) { /** @todo take filename from archive? */ size_t cchSuff = strlen(pOpts->pszSuff); Assert(cchSuff > 0); size_t cch = strlen(pOpts->szOutput); if ( cch <= cchSuff || strcmp(&pOpts->szOutput[cch - cchSuff], pOpts->pszSuff)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Input file does not end with: '%s'", pOpts->pszSuff); pOpts->szOutput[cch - cchSuff] = '\0'; if (!RTPathFilename(pOpts->szOutput)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: Input file name is all suffix."); } else { rc = RTStrCat(pOpts->szOutput, sizeof(pOpts->szOutput), pOpts->pszSuff); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error constructing output filename: %Rrc", rc); } /* Open the output file. */ uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; if (pOpts->fForce) fOpen |= RTFILE_O_CREATE_REPLACE; else fOpen |= RTFILE_O_CREATE; rc = RTVfsIoStrmOpenNormal(pOpts->szOutput, fOpen, phVfsIos); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening output file '%s': %Rrc", pOpts->szOutput, rc); } return RTEXITCODE_SUCCESS; }
/** * Recursively changes the file mode. * * @returns exit code * @param pOpts The mkdir option. * @param pszPath The path to start changing the mode of. */ static int rtCmdChModRecursive(RTCMDCHMODOPTS const *pOpts, const char *pszPath) { /* * Check if it's a directory first. If not, join the non-recursive code. */ int rc; uint32_t offError; RTFSOBJINFO ObjInfo; RTERRINFOSTATIC ErrInfo; bool const fUseChainApi = pOpts->fAlwaysUseChainApi || RTVfsChainIsSpec(pszPath); if (!fUseChainApi) { rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); if (RT_FAILURE(rc)) return RTMsgErrorExitFailure("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc); } else { rc = RTVfsChainQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_FAILURE(rc)) return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core); } if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode)) { /* * Don't bother redoing the above work if its not necessary. */ RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode); if (fNewMode != ObjInfo.Attr.fMode) return rtCmdChModOne(pOpts, pszPath); if (pOpts->enmNoiseLevel >= kRTCmdChModNoise_Verbose) RTPrintf("%s\n", pszPath); return RTEXITCODE_SUCCESS; } /* * For recursion we always use the VFS layer. */ RTVFSDIR hVfsDir; if (!fUseChainApi) { rc = RTVfsDirOpenNormal(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir); if (RT_FAILURE(rc)) return RTMsgErrorExitFailure("RTVfsDirOpenNormal failed on '%s': %Rrc", pszPath, rc); } else { rc = RTVfsChainOpenDir(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_FAILURE(rc)) return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core); } RTMsgError("Recursion is not yet implemented\n"); RTVfsDirRelease(hVfsDir); rc = VERR_NOT_IMPLEMENTED; return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }
/** * Changes the file mode of one file system object. * * @returns exit code * @param pOpts The chmod options. * @param pszPath The path to the file system object to change the * file mode of. */ static RTEXITCODE rtCmdChModOne(RTCMDCHMODOPTS const *pOpts, const char *pszPath) { int rc; RTFSOBJINFO ObjInfo; bool fChanges = false; if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszPath) ) { rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); if (RT_SUCCESS(rc)) { RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode); fChanges = fNewMode != ObjInfo.Attr.fMode; if (fChanges) { rc = RTPathSetMode(pszPath, fNewMode); if (RT_FAILURE(rc)) RTMsgError("RTPathSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc); } } else RTMsgError("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc); } else { RTVFSOBJ hVfsObj; uint32_t offError; RTERRINFOSTATIC ErrInfo; rc = RTVfsChainOpenObj(pszPath, RTFILE_O_ACCESS_ATTR_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_FOLLOW_LINK, &hVfsObj, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_SUCCESS(rc)) { rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING); if (RT_SUCCESS(rc)) { RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode); fChanges = fNewMode != ObjInfo.Attr.fMode; if (fChanges) { rc = RTVfsObjSetMode(hVfsObj, fNewMode, RTCHMOD_SET_ALL_MASK); if (RT_FAILURE(rc)) RTMsgError("RTVfsObjSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc); } } else RTVfsChainMsgError("RTVfsObjQueryInfo", pszPath, rc, offError, &ErrInfo.Core); RTVfsObjRelease(hVfsObj); } else RTVfsChainMsgError("RTVfsChainOpenObject", pszPath, rc, offError, &ErrInfo.Core); } if (RT_SUCCESS(rc)) { if (pOpts->enmNoiseLevel >= (fChanges ? kRTCmdChModNoise_Changes : kRTCmdChModNoise_Verbose)) RTPrintf("%s\n", pszPath); return RTEXITCODE_SUCCESS; } return RTEXITCODE_FAILURE; }
/** * Removes one directory. * * @returns exit code * @param pOpts The mkdir option. * @param pszDir The path to the new directory. */ static RTEXITCODE rtCmdRmDirOne(RTCMDRMDIROPTS const *pOpts, const char *pszDir) { int rc; if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) ) rc = RTDirRemove(pszDir); else { RTVFSDIR hVfsDir; const char *pszChild; uint32_t offError; RTERRINFOSTATIC ErrInfo; rc = RTVfsChainOpenParentDir(pszDir, 0 /*fOpen*/, &hVfsDir, &pszChild, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_SUCCESS(rc)) { rc = RTVfsDirRemoveDir(hVfsDir, pszChild, 0 /*fFlags*/); RTVfsDirRelease(hVfsDir); } else return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenParentDir", pszDir, rc, offError, &ErrInfo.Core); } if (RT_SUCCESS(rc)) { if (pOpts->fVerbose) RTPrintf("%s\n", pszDir); return RTEXITCODE_SUCCESS; } if ((rc == VERR_DIR_NOT_EMPTY || rc == VERR_SHARING_VIOLATION) && pOpts->fIgnoreNotEmpty) return RTEXITCODE_SUCCESS; /** @todo be verbose about this? */ if ((rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND) && pOpts->fIgnoreNonExisting) return RTEXITCODE_SUCCESS; /** @todo be verbose about this? */ return RTMsgErrorExitFailure("Failed to remove '%s': %Rrc", pszDir, rc); }
/** * A mini GZIP program. * * @returns Program exit code. * * @param cArgs The number of arguments. * @param papszArgs The argument vector. (Note that this may be * reordered, so the memory must be writable.) */ RTEXITCODE RTZipGzipCmd(unsigned cArgs, char **papszArgs) { /* * 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 }, { "--keep", 'k', 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 } }; RTGZIPCMDOPTS Opts; Opts.fAscii = false; Opts.fStdOut = false; Opts.fDecompress = false; Opts.fForce = false; Opts.fKeep = false; Opts.fList = false; Opts.fName = true; Opts.fQuiet = false; Opts.fRecursive = false; Opts.pszSuff = ".gz"; Opts.fTest = false; Opts.uLevel = 6; RTEXITCODE rcExit = RTEXITCODE_SUCCESS; unsigned cProcessed = 0; //RTVFSIOSTREAM hVfsStdOut= NIL_RTVFSIOSTREAM; RTGETOPTSTATE GetState; int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc); for (;;) { RTGETOPTUNION ValueUnion; int chOpt = RTGetOpt(&GetState, &ValueUnion); switch (chOpt) { 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; ValueUnion.psz = "-"; Opts.fStdOut = true; /* Fall thru. */ case VINF_GETOPT_NOT_OPTION: { if (!*Opts.pszSuff && !Opts.fStdOut) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --suffix option specified an empty string"); if (!Opts.fStdOut && RTVfsChainIsSpec(ValueUnion.psz)) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must use standard out with VFS chain specifications"); if (Opts.fName) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --name option has not yet been implemented. Use --no-name."); if (Opts.fAscii) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --ascii option has not yet been implemented."); if (Opts.fRecursive) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --recursive option has not yet been implemented."); /* Open the input file. */ RTVFSIOSTREAM hVfsSrc; RTEXITCODE rcExit2 = gzipOpenInput(ValueUnion.psz, &Opts, &hVfsSrc); if (rcExit2 == RTEXITCODE_SUCCESS) { if (Opts.fList) rcExit2 = gzipListFile(&hVfsSrc, &Opts); else if (Opts.fTest) rcExit2 = gzipTestFile(&hVfsSrc, &Opts); else { RTVFSIOSTREAM hVfsDst; rcExit2 = gzipOpenOutput(ValueUnion.psz, &Opts, &hVfsDst); if (rcExit2 == RTEXITCODE_SUCCESS) { if (Opts.fDecompress) rcExit2 = gzipDecompressFile(&hVfsSrc, &Opts, &hVfsDst); else rcExit2 = gzipCompressFile(&hVfsSrc, &Opts, &hVfsDst); RTVfsIoStrmRelease(hVfsDst); } } RTVfsIoStrmRelease(hVfsSrc); } if (rcExit2 != RTEXITCODE_SUCCESS) rcExit = rcExit2; cProcessed++; break; } case 'a': Opts.fAscii = true; break; case 'c': Opts.fStdOut = true; Opts.fKeep = true; break; case 'd': Opts.fDecompress = true; break; case 'f': Opts.fForce = true; break; case 'k': Opts.fKeep = true; break; case 'l': Opts.fList = true; break; case 'n': Opts.fName = false; break; case 'N': Opts.fName = true; break; case 'q': Opts.fQuiet = true; break; case 'r': Opts.fRecursive = true; break; case 'S': Opts.pszSuff = ValueUnion.psz; break; case 't': Opts.fTest = true; break; case 'v': Opts.fQuiet = false; break; case '1': Opts.uLevel = 1; break; case '2': Opts.uLevel = 2; break; case '3': Opts.uLevel = 3; break; case '4': Opts.uLevel = 4; break; case '5': Opts.uLevel = 5; break; case '6': Opts.uLevel = 6; break; case '7': Opts.uLevel = 7; break; case '8': Opts.uLevel = 8; break; case '9': Opts.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(chOpt, &ValueUnion); } } }
/** * Create one directory and any missing parent directories. * * @returns exit code * @param pOpts The mkdir option. * @param pszDir The path to the new directory. */ static int rtCmdRmDirOneWithParents(RTCMDRMDIROPTS const *pOpts, const char *pszDir) { /* We need a copy we can work with here. */ char *pszCopy = RTStrDup(pszDir); if (!pszCopy) return RTMsgErrorExitFailure("Out of string memory!"); int rc; if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) ) { size_t cchCopy = strlen(pszCopy); do { rc = RTDirRemove(pszCopy); if (RT_SUCCESS(rc)) { if (pOpts->fVerbose) RTPrintf("%s\n", pszCopy); } else if ((rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND) && pOpts->fIgnoreNonExisting) rc = VINF_SUCCESS; else { if ((rc == VERR_DIR_NOT_EMPTY || rc == VERR_SHARING_VIOLATION) && pOpts->fIgnoreNotEmpty) rc = VINF_SUCCESS; else RTMsgError("Failed to remove directory '%s': %Rrc", pszCopy, rc); break; } /* Strip off a component. */ while (cchCopy > 0 && RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; while (cchCopy > 0 && !RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; while (cchCopy > 0 && RTPATH_IS_SLASH(pszCopy[cchCopy - 1])) cchCopy--; pszCopy[cchCopy] = '\0'; } while (cchCopy > 0); } else { /* * Strip the final path element from the pszDir spec. */ char *pszFinalPath; char *pszSpec; uint32_t offError; rc = RTVfsChainSplitOffFinalPath(pszCopy, &pszSpec, &pszFinalPath, &offError); if (RT_SUCCESS(rc)) { /* * Open the root director/whatever. */ RTERRINFOSTATIC ErrInfo; RTVFSDIR hVfsBaseDir; if (pszSpec) { rc = RTVfsChainOpenDir(pszSpec, 0 /*fOpen*/, &hVfsBaseDir, &offError, RTErrInfoInitStatic(&ErrInfo)); if (RT_FAILURE(rc)) RTVfsChainMsgError("RTVfsChainOpenDir", pszSpec, rc, offError, &ErrInfo.Core); else if (!pszFinalPath) pszFinalPath = RTStrEnd(pszSpec, RTSTR_MAX); } else if (!RTPathStartsWithRoot(pszFinalPath)) { rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsBaseDir); if (RT_FAILURE(rc)) RTMsgError("Failed to open '.' (for %s): %Rrc", rc, pszFinalPath); } else { char *pszRoot = pszFinalPath; pszFinalPath = RTPathSkipRootSpec(pszFinalPath); char const chSaved = *pszFinalPath; *pszFinalPath = '\0'; rc = RTVfsDirOpenNormal(pszRoot, 0 /*fOpen*/, &hVfsBaseDir); *pszFinalPath = chSaved; if (RT_FAILURE(rc)) RTMsgError("Failed to open root dir for '%s': %Rrc", rc, pszRoot); } /* * Walk the path component by component, starting at the end. */ if (RT_SUCCESS(rc)) { size_t cchFinalPath = strlen(pszFinalPath); while (RT_SUCCESS(rc) && cchFinalPath > 0) { rc = RTVfsDirRemoveDir(hVfsBaseDir, pszFinalPath, 0 /*fFlags*/); if (RT_SUCCESS(rc)) { if (pOpts->fVerbose) RTPrintf("%s\n", pszCopy); } else if ((rc == VERR_PATH_NOT_FOUND || rc == VERR_FILE_NOT_FOUND) && pOpts->fIgnoreNonExisting) rc = VINF_SUCCESS; else { if ((rc == VERR_DIR_NOT_EMPTY || rc == VERR_SHARING_VIOLATION) && pOpts->fIgnoreNotEmpty) rc = VINF_SUCCESS; else if (pszSpec) RTMsgError("Failed to remove directory '%s:%s': %Rrc", pszSpec, pszFinalPath, rc); else RTMsgError("Failed to remove directory '%s': %Rrc", pszFinalPath, rc); break; } /* Strip off a component. */ while (cchFinalPath > 0 && RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; while (cchFinalPath > 0 && !RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; while (cchFinalPath > 0 && RTPATH_IS_SLASH(pszFinalPath[cchFinalPath - 1])) cchFinalPath--; pszFinalPath[cchFinalPath] = '\0'; } RTVfsDirRelease(hVfsBaseDir); } } else RTVfsChainMsgError("RTVfsChainOpenParentDir", pszCopy, rc, offError, NULL); } RTStrFree(pszCopy); return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }
int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, 0); 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); } } }