VirtualBox

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

Last change on this file since 105381 was 103385, checked in by vboxsync, 9 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
Line 
1/* $Id: filesplitter.cpp 103385 2024-02-15 12:30:38Z vboxsync $ */
2/** @file
3 * File splitter - Splits a text file according to ###### markers in it.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
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
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <errno.h>
37
38#include <iprt/string.h>
39#include <iprt/stdarg.h>
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45#ifndef S_ISDIR
46# define S_ISDIR(a_fMode) ( (S_IFMT & (a_fMode)) == S_IFDIR )
47#endif
48
49
50/**
51 * Calculates the line number for a file position.
52 *
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)
58{
59 unsigned long cLine = 0;
60 while ( *pcszContent
61 && (uintptr_t)pcszContent < (uintptr_t)pcszPos)
62 {
63 pcszContent = strchr(pcszContent, '\n');
64 if (!pcszContent)
65 break;
66 ++cLine;
67 ++pcszContent;
68 }
69
70 return cLine;
71}
72
73
74/**
75 * Writes an error message.
76 *
77 * @returns RTEXITCODE_FAILURE.
78 * @param pcszFormat Error message.
79 * @param ... Format argument referenced in the message.
80 */
81static int printErr(const char *pcszFormat, ...)
82{
83 va_list va;
84
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/**
95 * Opens the makefile list for writing.
96 *
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)
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
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/**
127 * Adds the given file to the makefile list.
128 *
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/**
149 * Closes the makefile list.
150 *
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/**
165 * Reads in a file.
166 *
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)
187 {
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)) )
192 {
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;
198
199 fclose(pFile);
200 return 0;
201 }
202 }
203
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}
213
214
215/**
216 * Checks whether the sub-file already exists and has the exact
217 * same content.
218 *
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{
226 struct stat FileStat;
227 if (stat(pcszFilename, &FileStat))
228 return false;
229 if ((size_t)FileStat.st_size < cchSubContent)
230 return false;
231
232 size_t cchExisting;
233 char *pszExisting;
234 int rc = readFile(pcszFilename, &pszExisting, &cchExisting);
235 if (rc)
236 return false;
237
238 bool fRc = cchExisting == cchSubContent
239 && !memcmp(pcszSubContent, pszExisting, cchSubContent);
240 free(pszExisting);
241
242 return fRc;
243}
244
245
246/**
247 * Writes out a sub-file.
248 *
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)
258#ifdef _MSC_VER
259 return printErr("Failed to open \"%s\" for writing: %s (win32: %d)\n", pcszFilename, strerror(errno), _doserrno);
260#else
261 return printErr("Failed to open \"%s\" for writing: %s\n", pcszFilename, strerror(errno));
262#endif
263
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));
268
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}
275
276
277/**
278 * Does the actual file splitting.
279 *
280 * @returns exit code.
281 * @param pcszOutDir Path to the output directory.
282 * @param pcszContent The content to split up.
283 * @param pFileList The file stream of the makefile list. Can be NULL.
284 */
285static int splitFile(const char *pcszOutDir, const char *pcszContent, FILE *pFileList)
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;
295
296 do
297 {
298 /* find begin marker */
299 const char *pcszBegin = strstr(pcszSearch, s_szBeginMarker);
300 if (!pcszBegin)
301 break;
302
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;
308
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)
313 return printErr("Can't parse filename after begin-file marker (line %lu).\n",
314 lineNumber(pcszContent, s_szBeginMarker));
315
316 /* find end marker */
317 const char *pcszEnd = strstr(pcszLineAfterBegin, s_szEndMarker);
318 if (!pcszEnd)
319 return printErr("No matching end-line marker for begin-file marker found (line %lu).\n",
320 lineNumber(pcszContent, s_szBeginMarker));
321
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");
327
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
342 if (!rc)
343 rc = addFileToMakefileList(pFileList, pszFilename);
344
345 free(pszFilename);
346
347 pcszSearch = pcszEnd;
348 } while (rc == 0);
349
350 printf("filesplitter: Out of %lu files: %lu rewritten, %lu unchanged. (%s)\n",
351 cFilesWritten + cFilesUnchanged, cFilesWritten, cFilesUnchanged, pcszOutDir);
352 return rc;
353}
354
355
356int main(int argc, char *argv[])
357{
358 int rc = 0;
359
360 if (argc == 3 || argc == 5)
361 {
362 struct stat DirStat;
363 if ( stat(argv[2], &DirStat) == 0
364 && S_ISDIR(DirStat.st_mode))
365 {
366 char *pszContent;
367 rc = readFile(argv[1], &pszContent, NULL);
368 if (!rc)
369 {
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);
379 free(pszContent);
380 }
381 }
382 else
383 rc = printErr("Given argument \"%s\" is not a valid directory.\n", argv[2]);
384 }
385 else
386 rc = printErr("Syntax error: usage: filesplitter <infile> <outdir> [<list.kmk> <kmkvar>]\n");
387 return rc;
388}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette