VirtualBox

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

Last change on this file since 67954 was 65237, checked in by vboxsync, 7 years ago

filesplitter.cpp: More error info on windows (had a weird EINVAL failure).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use