VirtualBox

source: vbox/trunk/src/bldprogs/filesplitter.cpp

Last change on this file was 103385, checked in by vboxsync, 4 months ago

filesplitter: pcszSearch is never NULL. bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.1 KB
RevLine 
[41624]1/* $Id: filesplitter.cpp 103385 2024-02-15 12:30:38Z vboxsync $ */
[23269]2/** @file
[41624]3 * File splitter - Splits a text file according to ###### markers in it.
[23269]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[23269]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[23269]26 */
27
[41624]28
[57353]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[23269]32#include <sys/types.h>
33#include <sys/stat.h>
34#include <stdio.h>
35#include <stdlib.h>
[41624]36#include <errno.h>
[23269]37
[41624]38#include <iprt/string.h>
39#include <iprt/stdarg.h>
40
41
[57353]42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
[41630]45#ifndef S_ISDIR
46# define S_ISDIR(a_fMode) ( (S_IFMT & (a_fMode)) == S_IFDIR )
47#endif
48
49
[41624]50/**
51 * Calculates the line number for a file position.
[48959]52 *
[41624]53 * @returns Line number.
54 * @param pcszContent The file content.
55 * @param pcszPos The current position.
56 */
57static unsigned long lineNumber(const char *pcszContent, const char *pcszPos)
[23269]58{
59 unsigned long cLine = 0;
[48959]60 while ( *pcszContent
[41624]61 && (uintptr_t)pcszContent < (uintptr_t)pcszPos)
[23269]62 {
[41624]63 pcszContent = strchr(pcszContent, '\n');
64 if (!pcszContent)
[23269]65 break;
66 ++cLine;
[41624]67 ++pcszContent;
[23269]68 }
69
70 return cLine;
71}
72
[41624]73
74/**
[48959]75 * Writes an error message.
76 *
[41624]77 * @returns RTEXITCODE_FAILURE.
78 * @param pcszFormat Error message.
79 * @param ... Format argument referenced in the message.
80 */
81static int printErr(const char *pcszFormat, ...)
[23269]82{
[41624]83 va_list va;
[23269]84
[41624]85 fprintf(stderr, "filesplitter: ");
86 va_start(va, pcszFormat);
87 vfprintf(stderr, pcszFormat, va);
88 va_end(va);
89
90 return RTEXITCODE_FAILURE;
91}
92
93
94/**
[48959]95 * Opens the makefile list for writing.
96 *
[41630]97 * @returns Exit code.
98 * @param pcszPath The path to the file.
99 * @param pcszVariableName The make variable name.
100 * @param ppFile Where to return the file stream.
101 */
102static int openMakefileList(const char *pcszPath, const char *pcszVariableName, FILE **ppFile)
103{
104 *ppFile = NULL;
105
106 FILE *pFile= fopen(pcszPath, "w");
107 if (!pFile)
[65237]108#ifdef _MSC_VER
109 return printErr("Failed to open \"%s\" for writing the file list: %s (win32: %d)\n",
110 pcszPath, strerror(errno), _doserrno);
111#else
112 return printErr("Failed to open \"%s\" for writing the file list: %s\n", pcszPath, strerror(errno));
113#endif
[41630]114
115 if (fprintf(pFile, "%s := \\\n", pcszVariableName) <= 0)
116 {
117 fclose(pFile);
118 return printErr("Error writing to the makefile list.\n");
119 }
120
121 *ppFile = pFile;
122 return 0;
123}
124
125
126/**
[48959]127 * Adds the given file to the makefile list.
128 *
[41630]129 * @returns Exit code.
130 * @param pFile The file stream of the makefile list.
131 * @param pszFilename The file name to add.
132 */
133static int addFileToMakefileList(FILE *pFile, char *pszFilename)
134{
135 if (pFile)
136 {
137 char *pszSlash = pszFilename;
138 while ((pszSlash = strchr(pszSlash, '\\')) != NULL)
139 *pszSlash++ = '/';
140
141 if (fprintf(pFile, "\t%s \\\n", pszFilename) <= 0)
142 return printErr("Error adding file to makefile list.\n");
143 }
144 return 0;
145}
146
147
148/**
[48959]149 * Closes the makefile list.
150 *
[41630]151 * @returns Exit code derived from @a rc.
152 * @param pFile The file stream of the makefile list.
153 * @param rc The current exit code.
154 */
155static int closeMakefileList(FILE *pFile, int rc)
156{
157 fprintf(pFile, "\n\n");
158 if (fclose(pFile))
159 return printErr("Error closing the file list file: %s\n", strerror(errno));
160 return rc;
161}
162
163
164/**
[48959]165 * Reads in a file.
166 *
[41624]167 * @returns Exit code.
168 * @param pcszFile The path to the file.
169 * @param ppszFile Where to return the buffer.
170 * @param pcchFile Where to return the file size.
171 */
172static int readFile(const char *pcszFile, char **ppszFile, size_t *pcchFile)
173{
174 FILE *pFile;
175 struct stat FileStat;
176 int rc;
177
178 if (stat(pcszFile, &FileStat))
179 return printErr("Failed to stat \"%s\": %s\n", pcszFile, strerror(errno));
180
181 pFile = fopen(pcszFile, "r");
182 if (!pFile)
183 return printErr("Failed to open \"%s\": %s\n", pcszFile, strerror(errno));
184
185 *ppszFile = (char *)malloc(FileStat.st_size + 1);
186 if (*ppszFile)
[29822]187 {
[41624]188 errno = 0;
189 size_t cbRead = fread(*ppszFile, 1, FileStat.st_size, pFile);
190 if ( cbRead <= (size_t)FileStat.st_size
191 && (cbRead > 0 || !ferror(pFile)) )
[23269]192 {
[41624]193 if (ftell(pFile) == FileStat.st_size) /* (\r\n vs \n in the DOS world) */
194 {
195 (*ppszFile)[cbRead] = '\0';
196 if (pcchFile)
197 *pcchFile = (size_t)cbRead;
[23269]198
[41624]199 fclose(pFile);
200 return 0;
201 }
[23269]202 }
203
[41624]204 rc = printErr("Error reading \"%s\": %s\n", pcszFile, strerror(errno));
205 free(*ppszFile);
206 *ppszFile = NULL;
207 }
208 else
209 rc = printErr("Failed to allocate %lu bytes\n", (unsigned long)(FileStat.st_size + 1));
210 fclose(pFile);
211 return rc;
212}
[23269]213
214
[41624]215/**
[48959]216 * Checks whether the sub-file already exists and has the exact
217 * same content.
218 *
[41624]219 * @returns @c true if the existing file matches exactly, otherwise @c false.
220 * @param pcszFilename The path to the file.
221 * @param pcszSubContent The content to write.
222 * @param cchSubContent The length of the content.
223 */
224static bool compareSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
225{
[48959]226 struct stat FileStat;
[41624]227 if (stat(pcszFilename, &FileStat))
228 return false;
229 if ((size_t)FileStat.st_size < cchSubContent)
230 return false;
[23269]231
[41624]232 size_t cchExisting;
233 char *pszExisting;
234 int rc = readFile(pcszFilename, &pszExisting, &cchExisting);
235 if (rc)
236 return false;
[23269]237
[41624]238 bool fRc = cchExisting == cchSubContent
239 && !memcmp(pcszSubContent, pszExisting, cchSubContent);
240 free(pszExisting);
[23269]241
[41624]242 return fRc;
243}
[23269]244
245
[41624]246/**
[48959]247 * Writes out a sub-file.
248 *
[41624]249 * @returns exit code.
250 * @param pcszFilename The path to the sub-file.
251 * @param pcszSubContent The content of the file.
252 * @param cchSubContent The size of the content.
253 */
254static int writeSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
255{
256 FILE *pFile = fopen(pcszFilename, "w");
257 if (!pFile)
[65237]258#ifdef _MSC_VER
259 return printErr("Failed to open \"%s\" for writing: %s (win32: %d)\n", pcszFilename, strerror(errno), _doserrno);
260#else
[41624]261 return printErr("Failed to open \"%s\" for writing: %s\n", pcszFilename, strerror(errno));
[65237]262#endif
[23269]263
[41624]264 errno = 0;
265 int rc = 0;
266 if (fwrite(pcszSubContent, cchSubContent, 1, pFile) != 1)
267 rc = printErr("Error writing \"%s\": %s\n", pcszFilename, strerror(errno));
[23269]268
[41624]269 errno = 0;
270 int rc2 = fclose(pFile);
271 if (rc2 == EOF)
272 rc = printErr("Error closing \"%s\": %s\n", pcszFilename, strerror(errno));
273 return rc;
274}
[23269]275
276
[41624]277/**
278 * Does the actual file splitting.
[48959]279 *
[41624]280 * @returns exit code.
281 * @param pcszOutDir Path to the output directory.
[48959]282 * @param pcszContent The content to split up.
[41630]283 * @param pFileList The file stream of the makefile list. Can be NULL.
[41624]284 */
[41630]285static int splitFile(const char *pcszOutDir, const char *pcszContent, FILE *pFileList)
[41624]286{
287 static char const s_szBeginMarker[] = "\n// ##### BEGINFILE \"";
288 static char const s_szEndMarker[] = "\n// ##### ENDFILE";
289 const size_t cchBeginMarker = sizeof(s_szBeginMarker) - 1;
290 const char *pcszSearch = pcszContent;
291 size_t const cchOutDir = strlen(pcszOutDir);
292 unsigned long cFilesWritten = 0;
293 unsigned long cFilesUnchanged = 0;
294 int rc = 0;
[23269]295
[41624]296 do
297 {
298 /* find begin marker */
299 const char *pcszBegin = strstr(pcszSearch, s_szBeginMarker);
300 if (!pcszBegin)
301 break;
[23269]302
[41624]303 /* find line after begin marker */
304 const char *pcszLineAfterBegin = strchr(pcszBegin + cchBeginMarker, '\n');
305 if (!pcszLineAfterBegin)
306 return printErr("No newline after begin-file marker found.\n");
307 ++pcszLineAfterBegin;
[23269]308
[41624]309 /* find filename end quote in begin marker line */
310 const char *pcszStartFilename = pcszBegin + cchBeginMarker;
311 const char *pcszEndQuote = (const char *)memchr(pcszStartFilename, '\"', pcszLineAfterBegin - pcszStartFilename);
312 if (!pcszEndQuote)
[48959]313 return printErr("Can't parse filename after begin-file marker (line %lu).\n",
[41624]314 lineNumber(pcszContent, s_szBeginMarker));
[23269]315
[41624]316 /* find end marker */
317 const char *pcszEnd = strstr(pcszLineAfterBegin, s_szEndMarker);
318 if (!pcszEnd)
[48959]319 return printErr("No matching end-line marker for begin-file marker found (line %lu).\n",
[41624]320 lineNumber(pcszContent, s_szBeginMarker));
[23269]321
[41624]322 /* construct output filename */
323 size_t cchFilename = pcszEndQuote - pcszStartFilename;
324 char *pszFilename = (char *)malloc(cchOutDir + 1 + cchFilename + 1);
325 if (!pszFilename)
326 return printErr("Can't allocate memory for filename.\n");
[23269]327
[41624]328 memcpy(pszFilename, pcszOutDir, cchOutDir);
329 pszFilename[cchOutDir] = '/';
330 memcpy(pszFilename + cchOutDir + 1, pcszStartFilename, cchFilename);
331 pszFilename[cchFilename + 1 + cchOutDir] = '\0';
332
333 /* Write the file only if necessary. */
334 if (compareSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin))
335 cFilesUnchanged++;
336 else
337 {
338 rc = writeSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin);
339 cFilesWritten++;
340 }
341
[41630]342 if (!rc)
343 rc = addFileToMakefileList(pFileList, pszFilename);
344
[41624]345 free(pszFilename);
346
347 pcszSearch = pcszEnd;
[103385]348 } while (rc == 0);
[41624]349
[48959]350 printf("filesplitter: Out of %lu files: %lu rewritten, %lu unchanged. (%s)\n",
[41624]351 cFilesWritten + cFilesUnchanged, cFilesWritten, cFilesUnchanged, pcszOutDir);
[23269]352 return rc;
353}
[41624]354
355
356int main(int argc, char *argv[])
357{
358 int rc = 0;
359
[41630]360 if (argc == 3 || argc == 5)
[41624]361 {
362 struct stat DirStat;
[41630]363 if ( stat(argv[2], &DirStat) == 0
[41624]364 && S_ISDIR(DirStat.st_mode))
365 {
366 char *pszContent;
367 rc = readFile(argv[1], &pszContent, NULL);
368 if (!rc)
369 {
[41630]370 FILE *pFileList = NULL;
371 if (argc == 5)
372 rc = openMakefileList(argv[3], argv[4], &pFileList);
373
374 if (argc < 4 || pFileList)
375 rc = splitFile(argv[2], pszContent, pFileList);
376
377 if (pFileList)
378 rc = closeMakefileList(pFileList, rc);
[41624]379 free(pszContent);
380 }
381 }
382 else
383 rc = printErr("Given argument \"%s\" is not a valid directory.\n", argv[2]);
384 }
385 else
[41630]386 rc = printErr("Syntax error: usage: filesplitter <infile> <outdir> [<list.kmk> <kmkvar>]\n");
[41624]387 return rc;
388}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use