VirtualBox

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

Last change on this file since 41766 was 41630, checked in by vboxsync, 13 years ago

filesplitter: optionally produce a list of files for kmk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.2 KB
Line 
1/* $Id: filesplitter.cpp 41630 2012-06-08 17:54:54Z vboxsync $ */
2/** @file
3 * File splitter - Splits a text file according to ###### markers in it.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 FILE *pFile;
213 if (stat(pcszFilename, &FileStat))
214 return false;
215 if ((size_t)FileStat.st_size < cchSubContent)
216 return false;
217
218 size_t cchExisting;
219 char *pszExisting;
220 int rc = readFile(pcszFilename, &pszExisting, &cchExisting);
221 if (rc)
222 return false;
223
224 bool fRc = cchExisting == cchSubContent
225 && !memcmp(pcszSubContent, pszExisting, cchSubContent);
226 free(pszExisting);
227
228 return fRc;
229}
230
231
232/**
233 * Writes out a sub-file.
234 *
235 * @returns exit code.
236 * @param pcszFilename The path to the sub-file.
237 * @param pcszSubContent The content of the file.
238 * @param cchSubContent The size of the content.
239 */
240static int writeSubFile(const char *pcszFilename, const char *pcszSubContent, size_t cchSubContent)
241{
242 FILE *pFile = fopen(pcszFilename, "w");
243 if (!pFile)
244 return printErr("Failed to open \"%s\" for writing: %s\n", pcszFilename, strerror(errno));
245
246 errno = 0;
247 int rc = 0;
248 if (fwrite(pcszSubContent, cchSubContent, 1, pFile) != 1)
249 rc = printErr("Error writing \"%s\": %s\n", pcszFilename, strerror(errno));
250
251 errno = 0;
252 int rc2 = fclose(pFile);
253 if (rc2 == EOF)
254 rc = printErr("Error closing \"%s\": %s\n", pcszFilename, strerror(errno));
255 return rc;
256}
257
258
259/**
260 * Does the actual file splitting.
261 *
262 * @returns exit code.
263 * @param pcszOutDir Path to the output directory.
264 * @param pcszContent The content to split up.
265 * @param pFileList The file stream of the makefile list. Can be NULL.
266 */
267static int splitFile(const char *pcszOutDir, const char *pcszContent, FILE *pFileList)
268{
269 static char const s_szBeginMarker[] = "\n// ##### BEGINFILE \"";
270 static char const s_szEndMarker[] = "\n// ##### ENDFILE";
271 const size_t cchBeginMarker = sizeof(s_szBeginMarker) - 1;
272 const char *pcszSearch = pcszContent;
273 size_t const cchOutDir = strlen(pcszOutDir);
274 unsigned long cFilesWritten = 0;
275 unsigned long cFilesUnchanged = 0;
276 int rc = 0;
277
278 do
279 {
280 /* find begin marker */
281 const char *pcszBegin = strstr(pcszSearch, s_szBeginMarker);
282 if (!pcszBegin)
283 break;
284
285 /* find line after begin marker */
286 const char *pcszLineAfterBegin = strchr(pcszBegin + cchBeginMarker, '\n');
287 if (!pcszLineAfterBegin)
288 return printErr("No newline after begin-file marker found.\n");
289 ++pcszLineAfterBegin;
290
291 /* find filename end quote in begin marker line */
292 const char *pcszStartFilename = pcszBegin + cchBeginMarker;
293 const char *pcszEndQuote = (const char *)memchr(pcszStartFilename, '\"', pcszLineAfterBegin - pcszStartFilename);
294 if (!pcszEndQuote)
295 return printErr("Can't parse filename after begin-file marker (line %lu).\n",
296 lineNumber(pcszContent, s_szBeginMarker));
297
298 /* find end marker */
299 const char *pcszEnd = strstr(pcszLineAfterBegin, s_szEndMarker);
300 if (!pcszEnd)
301 return printErr("No matching end-line marker for begin-file marker found (line %lu).\n",
302 lineNumber(pcszContent, s_szBeginMarker));
303
304 /* construct output filename */
305 size_t cchFilename = pcszEndQuote - pcszStartFilename;
306 char *pszFilename = (char *)malloc(cchOutDir + 1 + cchFilename + 1);
307 if (!pszFilename)
308 return printErr("Can't allocate memory for filename.\n");
309
310 memcpy(pszFilename, pcszOutDir, cchOutDir);
311 pszFilename[cchOutDir] = '/';
312 memcpy(pszFilename + cchOutDir + 1, pcszStartFilename, cchFilename);
313 pszFilename[cchFilename + 1 + cchOutDir] = '\0';
314
315 /* Write the file only if necessary. */
316 if (compareSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin))
317 cFilesUnchanged++;
318 else
319 {
320 rc = writeSubFile(pszFilename, pcszLineAfterBegin, pcszEnd - pcszLineAfterBegin);
321 cFilesWritten++;
322 }
323
324 if (!rc)
325 rc = addFileToMakefileList(pFileList, pszFilename);
326
327 free(pszFilename);
328
329 pcszSearch = pcszEnd;
330 } while (rc == 0 && pcszSearch);
331
332 printf("filesplitter: Out of %lu files: %lu rewritten, %lu unchanged. (%s)\n",
333 cFilesWritten + cFilesUnchanged, cFilesWritten, cFilesUnchanged, pcszOutDir);
334 return rc;
335}
336
337
338int main(int argc, char *argv[])
339{
340 int rc = 0;
341
342 if (argc == 3 || argc == 5)
343 {
344 struct stat DirStat;
345 if ( stat(argv[2], &DirStat) == 0
346 && S_ISDIR(DirStat.st_mode))
347 {
348 char *pszContent;
349 rc = readFile(argv[1], &pszContent, NULL);
350 if (!rc)
351 {
352 FILE *pFileList = NULL;
353 if (argc == 5)
354 rc = openMakefileList(argv[3], argv[4], &pFileList);
355
356 if (argc < 4 || pFileList)
357 rc = splitFile(argv[2], pszContent, pFileList);
358
359 if (pFileList)
360 rc = closeMakefileList(pFileList, rc);
361 free(pszContent);
362 }
363 }
364 else
365 rc = printErr("Given argument \"%s\" is not a valid directory.\n", argv[2]);
366 }
367 else
368 rc = printErr("Syntax error: usage: filesplitter <infile> <outdir> [<list.kmk> <kmkvar>]\n");
369 return rc;
370}
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