VirtualBox

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

Last change on this file since 62854 was 62537, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.5 KB
Line 
1/* $Id: filesplitter.cpp 62537 2016-07-22 19:32: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 return printErr("Failed to open \"%s\" for writing the file list");
99
100 if (fprintf(pFile, "%s := \\\n", pcszVariableName) <= 0)
101 {
102 fclose(pFile);
103 return printErr("Error writing to the makefile list.\n");
104 }
105
106 *ppFile = pFile;
107 return 0;
108}
109
110
111/**
112 * Adds the given file to the makefile list.
113 *
114 * @returns Exit code.
115 * @param pFile The file stream of the makefile list.
116 * @param pszFilename The file name to add.
117 */
118static int addFileToMakefileList(FILE *pFile, char *pszFilename)
119{
120 if (pFile)
121 {
122 char *pszSlash = pszFilename;
123 while ((pszSlash = strchr(pszSlash, '\\')) != NULL)
124 *pszSlash++ = '/';
125
126 if (fprintf(pFile, "\t%s \\\n", pszFilename) <= 0)
127 return printErr("Error adding file to makefile list.\n");
128 }
129 return 0;
130}
131
132
133/**
134 * Closes the makefile list.
135 *
136 * @returns Exit code derived from @a rc.
137 * @param pFile The file stream of the makefile list.
138 * @param rc The current exit code.
139 */
140static int closeMakefileList(FILE *pFile, int rc)
141{
142 fprintf(pFile, "\n\n");
143 if (fclose(pFile))
144 return printErr("Error closing the file list file: %s\n", strerror(errno));
145 return rc;
146}
147
148
149/**
150 * Reads in a file.
151 *
152 * @returns Exit code.
153 * @param pcszFile The path to the file.
154 * @param ppszFile Where to return the buffer.
155 * @param pcchFile Where to return the file size.
156 */
157static int readFile(const char *pcszFile, char **ppszFile, size_t *pcchFile)
158{
159 FILE *pFile;
160 struct stat FileStat;
161 int rc;
162
163 if (stat(pcszFile, &FileStat))
164 return printErr("Failed to stat \"%s\": %s\n", pcszFile, strerror(errno));
165
166 pFile = fopen(pcszFile, "r");
167 if (!pFile)
168 return printErr("Failed to open \"%s\": %s\n", pcszFile, strerror(errno));
169
170 *ppszFile = (char *)malloc(FileStat.st_size + 1);
171 if (*ppszFile)
172 {
173 errno = 0;
174 size_t cbRead = fread(*ppszFile, 1, FileStat.st_size, pFile);
175 if ( cbRead <= (size_t)FileStat.st_size
176 && (cbRead > 0 || !ferror(pFile)) )
177 {
178 if (ftell(pFile) == FileStat.st_size) /* (\r\n vs \n in the DOS world) */
179 {
180 (*ppszFile)[cbRead] = '\0';
181 if (pcchFile)
182 *pcchFile = (size_t)cbRead;
183
184 fclose(pFile);
185 return 0;
186 }
187 }
188
189 rc = printErr("Error reading \"%s\": %s\n", pcszFile, strerror(errno));
190 free(*ppszFile);
191 *ppszFile = NULL;
192 }
193 else
194 rc = printErr("Failed to allocate %lu bytes\n", (unsigned long)(FileStat.st_size + 1));
195 fclose(pFile);
196 return rc;
197}
198
199
200/**
201 * Checks whether the sub-file already exists and has the exact
202 * same content.
203 *
204 * @returns @c true if the existing file matches exactly, otherwise @c false.
205 * @param pcszFilename The path to the file.
206 * @param pcszSubContent The content to write.
207 * @param cchSubContent The length of the content.
208 */
209static bool compareSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
210{
211 struct stat FileStat;
212 if (stat(pcszFilename, &FileStat))
213 return false;
214 if ((size_t)FileStat.st_size < cchSubContent)
215 return false;
216
217 size_t cchExisting;
218 char *pszExisting;
219 int rc = readFile(pcszFilename, &pszExisting, &cchExisting);
220 if (rc)
221 return false;
222
223 bool fRc = cchExisting == cchSubContent
224 && !memcmp(pcszSubContent, pszExisting, cchSubContent);
225 free(pszExisting);
226
227 return fRc;
228}
229
230
231/**
232 * Writes out a sub-file.
233 *
234 * @returns exit code.
235 * @param pcszFilename The path to the sub-file.
236 * @param pcszSubContent The content of the file.
237 * @param cchSubContent The size of the content.
238 */
239static int writeSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
240{
241 FILE *pFile = fopen(pcszFilename, "w");
242 if (!pFile)
243 return printErr("Failed to open \"%s\" for writing: %s\n", pcszFilename, strerror(errno));
244
245 errno = 0;
246 int rc = 0;
247 if (fwrite(pcszSubContent, cchSubContent, 1, pFile) != 1)
248 rc = printErr("Error writing \"%s\": %s\n", pcszFilename, strerror(errno));
249
250 errno = 0;
251 int rc2 = fclose(pFile);
252 if (rc2 == EOF)
253 rc = printErr("Error closing \"%s\": %s\n", pcszFilename, strerror(errno));
254 return rc;
255}
256
257
258/**
259 * Does the actual file splitting.
260 *
261 * @returns exit code.
262 * @param pcszOutDir Path to the output directory.
263 * @param pcszContent The content to split up.
264 * @param pFileList The file stream of the makefile list. Can be NULL.
265 */
266static int splitFile(const char *pcszOutDir, const char *pcszContent, FILE *pFileList)
267{
268 static char const s_szBeginMarker[] = "\n// ##### BEGINFILE \"";
269 static char const s_szEndMarker[] = "\n// ##### ENDFILE";
270 const size_t cchBeginMarker = sizeof(s_szBeginMarker) - 1;
271 const char *pcszSearch = pcszContent;
272 size_t const cchOutDir = strlen(pcszOutDir);
273 unsigned long cFilesWritten = 0;
274 unsigned long cFilesUnchanged = 0;
275 int rc = 0;
276
277 do
278 {
279 /* find begin marker */
280 const char *pcszBegin = strstr(pcszSearch, s_szBeginMarker);
281 if (!pcszBegin)
282 break;
283
284 /* find line after begin marker */
285 const char *pcszLineAfterBegin = strchr(pcszBegin + cchBeginMarker, '\n');
286 if (!pcszLineAfterBegin)
287 return printErr("No newline after begin-file marker found.\n");
288 ++pcszLineAfterBegin;
289
290 /* find filename end quote in begin marker line */
291 const char *pcszStartFilename = pcszBegin + cchBeginMarker;
292 const char *pcszEndQuote = (const char *)memchr(pcszStartFilename, '\"', pcszLineAfterBegin - pcszStartFilename);
293 if (!pcszEndQuote)
294 return printErr("Can't parse filename after begin-file marker (line %lu).\n",
295 lineNumber(pcszContent, s_szBeginMarker));
296
297 /* find end marker */
298 const char *pcszEnd = strstr(pcszLineAfterBegin, s_szEndMarker);
299 if (!pcszEnd)
300 return printErr("No matching end-line marker for begin-file marker found (line %lu).\n",
301 lineNumber(pcszContent, s_szBeginMarker));
302
303 /* construct output filename */
304 size_t cchFilename = pcszEndQuote - pcszStartFilename;
305 char *pszFilename = (char *)malloc(cchOutDir + 1 + cchFilename + 1);
306 if (!pszFilename)
307 return printErr("Can't allocate memory for filename.\n");
308
309 memcpy(pszFilename, pcszOutDir, cchOutDir);
310 pszFilename[cchOutDir] = '/';
311 memcpy(pszFilename + cchOutDir + 1, pcszStartFilename, cchFilename);
312 pszFilename[cchFilename + 1 + cchOutDir] = '\0';
313
314 /* Write the file only if necessary. */
315 if (compareSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin))
316 cFilesUnchanged++;
317 else
318 {
319 rc = writeSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin);
320 cFilesWritten++;
321 }
322
323 if (!rc)
324 rc = addFileToMakefileList(pFileList, pszFilename);
325
326 free(pszFilename);
327
328 pcszSearch = pcszEnd;
329 } while (rc == 0 && pcszSearch);
330
331 printf("filesplitter: Out of %lu files: %lu rewritten, %lu unchanged. (%s)\n",
332 cFilesWritten + cFilesUnchanged, cFilesWritten, cFilesUnchanged, pcszOutDir);
333 return rc;
334}
335
336
337int main(int argc, char *argv[])
338{
339 int rc = 0;
340
341 if (argc == 3 || argc == 5)
342 {
343 struct stat DirStat;
344 if ( stat(argv[2], &DirStat) == 0
345 && S_ISDIR(DirStat.st_mode))
346 {
347 char *pszContent;
348 rc = readFile(argv[1], &pszContent, NULL);
349 if (!rc)
350 {
351 FILE *pFileList = NULL;
352 if (argc == 5)
353 rc = openMakefileList(argv[3], argv[4], &pFileList);
354
355 if (argc < 4 || pFileList)
356 rc = splitFile(argv[2], pszContent, pFileList);
357
358 if (pFileList)
359 rc = closeMakefileList(pFileList, rc);
360 free(pszContent);
361 }
362 }
363 else
364 rc = printErr("Given argument \"%s\" is not a valid directory.\n", argv[2]);
365 }
366 else
367 rc = printErr("Syntax error: usage: filesplitter <infile> <outdir> [<list.kmk> <kmkvar>]\n");
368 return rc;
369}
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