filesplitter.cpp revision eef2e5c5d3dce76f732cc55e434a5e6e85033115
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * File splitter - Splits a text file according to ###### markers in it.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Copyright (C) 2006-2012 Oracle Corporation
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * available from http://www.virtualbox.org. This file is free software;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * General Public License (GPL) as published by the Free Software
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync/*******************************************************************************
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync* Header Files *
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync*******************************************************************************/
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync/*******************************************************************************
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync* Defined Constants And Macros *
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync*******************************************************************************/
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync# define S_ISDIR(a_fMode) ( (S_IFMT & (a_fMode)) == S_IFDIR )
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Calculates the line number for a file position.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns Line number.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszContent The file content.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszPos The current position.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic unsigned long lineNumber(const char *pcszContent, const char *pcszPos)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync unsigned long cLine = 0;
3a343ca21a267ec3c54e2317e2ed18fe99b8ebbbvboxsync * Writes an error message.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns RTEXITCODE_FAILURE.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszFormat Error message.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param ... Format argument referenced in the message.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Opens the makefile list for writing.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns Exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszPath The path to the file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszVariableName The make variable name.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param ppFile Where to return the file stream.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int openMakefileList(const char *pcszPath, const char *pcszVariableName, FILE **ppFile)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Failed to open \"%s\" for writing the file list");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (fprintf(pFile, "%s := \\\n", pcszVariableName) <= 0)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Error writing to the makefile list.\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Adds the given file to the makefile list.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns Exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pFile The file stream of the makefile list.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pszFilename The file name to add.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int addFileToMakefileList(FILE *pFile, char *pszFilename)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync while ((pszSlash = strchr(pszSlash, '\\')) != NULL)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Error adding file to makefile list.\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Closes the makefile list.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns Exit code derived from @a rc.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pFile The file stream of the makefile list.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param rc The current exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Error closing the file list file: %s\n", strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Reads in a file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns Exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszFile The path to the file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param ppszFile Where to return the buffer.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcchFile Where to return the file size.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int readFile(const char *pcszFile, char **ppszFile, size_t *pcchFile)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Failed to stat \"%s\": %s\n", pcszFile, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Failed to open \"%s\": %s\n", pcszFile, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync size_t cbRead = fread(*ppszFile, 1, FileStat.st_size, pFile);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (ftell(pFile) == FileStat.st_size) /* (\r\n vs \n in the DOS world) */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = printErr("Error reading \"%s\": %s\n", pcszFile, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = printErr("Failed to allocate %lu bytes\n", (unsigned long)(FileStat.st_size + 1));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Checks whether the sub-file already exists and has the exact
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * same content.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns @c true if the existing file matches exactly, otherwise @c false.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszFilename The path to the file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszSubContent The content to write.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cchSubContent The length of the content.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic bool compareSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync int rc = readFile(pcszFilename, &pszExisting, &cchExisting);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return false;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync && !memcmp(pcszSubContent, pszExisting, cchSubContent);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Writes out a sub-file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszFilename The path to the sub-file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszSubContent The content of the file.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param cchSubContent The size of the content.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int writeSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Failed to open \"%s\" for writing: %s\n", pcszFilename, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (fwrite(pcszSubContent, cchSubContent, 1, pFile) != 1)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = printErr("Error writing \"%s\": %s\n", pcszFilename, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = printErr("Error closing \"%s\": %s\n", pcszFilename, strerror(errno));
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * Does the actual file splitting.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @returns exit code.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszOutDir Path to the output directory.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pcszContent The content to split up.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync * @param pFileList The file stream of the makefile list. Can be NULL.
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsyncstatic int splitFile(const char *pcszOutDir, const char *pcszContent, FILE *pFileList)
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync static char const s_szBeginMarker[] = "\n// ##### BEGINFILE \"";
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync static char const s_szEndMarker[] = "\n// ##### ENDFILE";
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const size_t cchBeginMarker = sizeof(s_szBeginMarker) - 1;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync unsigned long cFilesWritten = 0;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync unsigned long cFilesUnchanged = 0;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* find begin marker */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const char *pcszBegin = strstr(pcszSearch, s_szBeginMarker);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* find line after begin marker */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const char *pcszLineAfterBegin = strchr(pcszBegin + cchBeginMarker, '\n');
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("No newline after begin-file marker found.\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* find filename end quote in begin marker line */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const char *pcszStartFilename = pcszBegin + cchBeginMarker;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const char *pcszEndQuote = (const char *)memchr(pcszStartFilename, '\"', pcszLineAfterBegin - pcszStartFilename);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Can't parse filename after begin-file marker (line %lu).\n",
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* find end marker */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync const char *pcszEnd = strstr(pcszLineAfterBegin, s_szEndMarker);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("No matching end-line marker for begin-file marker found (line %lu).\n",
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* construct output filename */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync size_t cchFilename = pcszEndQuote - pcszStartFilename;
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync char *pszFilename = (char *)malloc(cchOutDir + 1 + cchFilename + 1);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync return printErr("Can't allocate memory for filename.\n");
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync memcpy(pszFilename + cchOutDir + 1, pcszStartFilename, cchFilename);
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync /* Write the file only if necessary. */
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync if (compareSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin))
9055f61bb57d2a625c6434d55beac7565c3b3c0dvboxsync rc = writeSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = addFileToMakefileList(pFileList, pszFilename);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync printf("filesplitter: Out of %lu files: %lu rewritten, %lu unchanged. (%s)\n",
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync cFilesWritten + cFilesUnchanged, cFilesWritten, cFilesUnchanged, pcszOutDir);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = openMakefileList(argv[3], argv[4], &pFileList);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = printErr("Given argument \"%s\" is not a valid directory.\n", argv[2]);
340ee06f35257fee1bd68223ab3504cf2b1d0c3evboxsync rc = printErr("Syntax error: usage: filesplitter <infile> <outdir> [<list.kmk> <kmkvar>]\n");