VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/append.c@ 3171

Last change on this file since 3171 was 3171, checked in by bird, 7 years ago

kmk_append: do own buffering so we can outsource the writing on windows where file system access sucks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: append.c 3171 2018-03-21 13:26:36Z bird $ */
2/** @file
3 * kMk Builtin command - append text to file.
4 */
5
6/*
7 * Copyright (c) 2005-2010 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#ifndef kmk_builtin_append
30# include "makeint.h"
31# include "filedef.h"
32# include "variable.h"
33#else
34# include "config.h"
35#endif
36#include <string.h>
37#include <stdio.h>
38#include <fcntl.h>
39#ifdef HAVE_UNISTD_H
40# include <unistd.h>
41#endif
42#ifdef _MSC_VER
43# include <io.h>
44#endif
45#ifdef HAVE_ALLOCA_H
46# include <alloca.h>
47#endif
48#include "err.h"
49#include "kmkbuiltin.h"
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55#define STR_TUPLE(a_sz) a_sz, (sizeof(a_sz) - 1)
56
57/** No-inherit open flag. */
58#ifdef _O_NOINHERIT
59# define MY_O_NOINHERIT _O_NOINHERIT
60#elif defined(O_NOINHERIT)
61# define MY_O_NOINHERIT O_NOINHERIT
62#elif defined(O_CLOEXEC)
63# define MY_O_NOINHERIT O_CLOEXEC
64#else
65# define MY_O_NOINHERIT 0
66#endif
67
68/** Binary mode open flag. */
69#ifdef _O_BINARY
70# define MY_O_BINARY _O_BINARY
71#elif defined(O_BINARY)
72# define MY_O_BINARY O_BINARY
73#else
74# define MY_O_BINARY 0
75#endif
76
77
78/*********************************************************************************************************************************
79* Structures and Typedefs *
80*********************************************************************************************************************************/
81/**
82 * Append output buffer.
83 */
84typedef struct KMKBUILTINAPPENDBUF
85{
86 /** Buffer pointer. */
87 char *pszBuf;
88 /** The buffer allocation size. */
89 size_t cbBuf;
90 /** The current buffer offset. */
91 size_t offBuf;
92 /** Set if we ran out of memory. */
93 int fOutOfMemory;
94} KMKBUILTINAPPENDBUF;
95
96
97/**
98 * Appends a substring to the output buffer.
99 *
100 * @param pBuf The output buffer.
101 * @param pch The substring pointer.
102 * @param cch The substring length.
103 */
104static void write_to_buf(KMKBUILTINAPPENDBUF *pBuf, const char *pch, size_t cch)
105{
106 size_t const offCur = pBuf->offBuf;
107 size_t offNew = offCur + cch;
108
109 if (offNew >= pBuf->cbBuf)
110 {
111 size_t cbNew = offNew + 1 + 256;
112 void *pvNew;
113 cbNew = (cbNew + 511) & ~(size_t)511;
114 pvNew = realloc(pBuf->pszBuf, cbNew);
115 if (pvNew)
116 pBuf->pszBuf = (char *)pvNew;
117 else
118 {
119 free(pBuf->pszBuf);
120 pBuf->pszBuf = NULL;
121 pBuf->cbBuf = 0;
122 pBuf->fOutOfMemory = 1;
123 return;
124 }
125 }
126
127 memcpy(&pBuf->pszBuf[offCur], pch, cch);
128 pBuf->pszBuf[offNew] = '\0';
129 pBuf->offBuf = offNew;
130}
131
132/**
133 * Adds a string to the output buffer.
134 *
135 * @param pBuf The output buffer.
136 * @param psz The string.
137 */
138static void string_to_buf(KMKBUILTINAPPENDBUF *pBuf, const char *psz)
139{
140 write_to_buf(pBuf, psz, strlen(psz));
141}
142
143
144/**
145 * Prints the usage and return 1.
146 */
147static int kmk_builtin_append_usage(const char *arg0, FILE *pf)
148{
149 fprintf(pf,
150 "usage: %s [-dcnNtv] file [string ...]\n"
151 " or: %s --version\n"
152 " or: %s --help\n"
153 "\n"
154 "Options:\n"
155 " -d Enclose the output in define ... endef, taking the name from\n"
156 " the first argument following the file name.\n"
157 " -c Output the command for specified target(s). [builtin only]\n"
158 " -i look for --insert-command=trg and --insert-variable=var. [builtin only]\n"
159 " -n Insert a newline between the strings.\n"
160 " -N Suppress the trailing newline.\n"
161 " -t Truncate the file instead of appending\n"
162 " -v Output the value(s) for specified variable(s). [builtin only]\n"
163 ,
164 arg0, arg0, arg0);
165 return 1;
166}
167
168/**
169 * Appends text to a textfile, creating the textfile if necessary.
170 */
171int kmk_builtin_append(int argc, char **argv, char **envp)
172{
173 const char *pszFilename;
174 int rc = 88;
175 int i;
176 int fFirst;
177 int fNewline = 0;
178 int fNoTrailingNewline = 0;
179 int fTruncate = 0;
180 int fDefine = 0;
181 int fVariables = 0;
182 int fCommands = 0;
183#ifndef kmk_builtin_append
184 int fLookForInserts = 0;
185#endif
186#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
187 static const char s_szNewLine[] = "\r\n";
188#else
189 static const char s_szNewLine[] = "\n";
190#endif
191 KMKBUILTINAPPENDBUF OutBuf = { NULL, 0, 0, 0 };
192
193 g_progname = argv[0];
194
195 /*
196 * Parse options.
197 */
198 i = 1;
199 while (i < argc
200 && argv[i][0] == '-'
201 && argv[i][1] != '\0' /* '-' is a file */
202 && strchr("-cdinNtv", argv[i][1]) /* valid option char */
203 )
204 {
205 char *psz = &argv[i][1];
206 if (*psz != '-')
207 {
208 do
209 {
210 switch (*psz)
211 {
212 case 'c':
213 if (fVariables)
214 {
215 errx(1, "Option '-c' clashes with '-v'.");
216 return kmk_builtin_append_usage(argv[0], stderr);
217 }
218#ifndef kmk_builtin_append
219 fCommands = 1;
220 break;
221#else
222 errx(1, "Option '-c' isn't supported in external mode.");
223 return kmk_builtin_append_usage(argv[0], stderr);
224#endif
225 case 'd':
226 if (fVariables)
227 {
228 errx(1, "Option '-d' must come before '-v'!");
229 return kmk_builtin_append_usage(argv[0], stderr);
230 }
231 fDefine = 1;
232 break;
233 case 'i':
234 if (fVariables || fCommands)
235 {
236 errx(1, fVariables ? "Option '-i' clashes with '-v'." : "Option '-i' clashes with '-c'.");
237 return kmk_builtin_append_usage(argv[0], stderr);
238 }
239#ifndef kmk_builtin_append
240 fLookForInserts = 1;
241 break;
242#else
243 errx(1, "Option '-C' isn't supported in external mode.");
244 return kmk_builtin_append_usage(argv[0], stderr);
245#endif
246 case 'n':
247 fNewline = 1;
248 break;
249 case 'N':
250 fNoTrailingNewline = 1;
251 break;
252 case 't':
253 fTruncate = 1;
254 break;
255 case 'v':
256 if (fCommands)
257 {
258 errx(1, "Option '-v' clashes with '-c'.");
259 return kmk_builtin_append_usage(argv[0], stderr);
260 }
261#ifndef kmk_builtin_append
262 fVariables = 1;
263 break;
264#else
265 errx(1, "Option '-v' isn't supported in external mode.");
266 return kmk_builtin_append_usage(argv[0], stderr);
267#endif
268 default:
269 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
270 return kmk_builtin_append_usage(argv[0], stderr);
271 }
272 } while (*++psz);
273 }
274 else if (!strcmp(psz, "-help"))
275 {
276 kmk_builtin_append_usage(argv[0], stdout);
277 return 0;
278 }
279 else if (!strcmp(psz, "-version"))
280 return kbuild_version(argv[0]);
281 else
282 break;
283 i++;
284 }
285
286 /*
287 * Take down the filename.
288 */
289 if (i + fDefine < argc)
290 pszFilename = argv[i++];
291 else
292 {
293 if (i <= argc)
294 errx(1, "missing filename!");
295 else
296 errx(1, "missing define name!");
297 return kmk_builtin_append_usage(argv[0], stderr);
298 }
299
300 /* Start of no-return zone! */
301
302 /*
303 * Start define?
304 */
305 if (fDefine)
306 {
307 write_to_buf(&OutBuf, STR_TUPLE("define "));
308 string_to_buf(&OutBuf, argv[i]);
309 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
310 i++;
311 }
312
313 /*
314 * Append the argument strings to the file
315 */
316 fFirst = 1;
317 for (; i < argc; i++)
318 {
319 const char *psz = argv[i];
320 size_t cch = strlen(psz);
321 if (!fFirst)
322 {
323 if (fNewline)
324 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
325 else
326 write_to_buf(&OutBuf, STR_TUPLE(" "));
327 }
328#ifndef kmk_builtin_append
329 if (fCommands)
330 {
331 char *pszOldBuf;
332 unsigned cchOldBuf;
333 char *pchEnd;
334
335 install_variable_buffer(&pszOldBuf, &cchOldBuf);
336
337 pchEnd = func_commands(variable_buffer, &argv[i], "commands");
338 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
339
340 restore_variable_buffer(pszOldBuf, cchOldBuf);
341 }
342 else if (fVariables)
343 {
344 struct variable *pVar = lookup_variable(psz, cch);
345 if (!pVar)
346 continue;
347 if ( !pVar->recursive
348 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
349 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
350 else
351 {
352 char *pszExpanded = allocated_variable_expand(pVar->value);
353 string_to_buf(&OutBuf, pszExpanded);
354 free(pszExpanded);
355 }
356 }
357 else if (fLookForInserts && strncmp(psz, "--insert-command=", 17) == 0)
358 {
359 char *pszOldBuf;
360 unsigned cchOldBuf;
361 char *pchEnd;
362
363 install_variable_buffer(&pszOldBuf, &cchOldBuf);
364
365 psz += 17;
366 pchEnd = func_commands(variable_buffer, (char **)&psz, "commands");
367 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
368
369 restore_variable_buffer(pszOldBuf, cchOldBuf);
370 }
371 else if (fLookForInserts && strncmp(psz, "--insert-variable=", 18) == 0)
372 {
373 struct variable *pVar = lookup_variable(psz + 18, cch);
374 if (!pVar)
375 continue;
376 if ( !pVar->recursive
377 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
378 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
379 else
380 {
381 char *pszExpanded = allocated_variable_expand(pVar->value);
382 string_to_buf(&OutBuf, pszExpanded);
383 free(pszExpanded);
384 }
385 }
386 else
387#endif
388 write_to_buf(&OutBuf, psz, cch);
389 fFirst = 0;
390 }
391
392 /*
393 * End the define?
394 */
395 if (fDefine)
396 {
397 if (fFirst)
398 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
399 write_to_buf(&OutBuf, STR_TUPLE("endef"));
400 }
401
402 /*
403 * Add final newline (unless supressed) and check for errors.
404 */
405 if (!fNoTrailingNewline)
406 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
407
408 /*
409 * Write the buffer (unless we ran out of heap already).
410 */
411 if (!OutBuf.fOutOfMemory)
412 {
413 int fd = open(pszFilename,
414 fTruncate ? O_WRONLY | O_TRUNC | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY
415 : O_WRONLY | O_APPEND | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY,
416 0666);
417 if (fd >= 0)
418 {
419 ssize_t cbWritten = write(fd, OutBuf.pszBuf, OutBuf.offBuf);
420 if (cbWritten == (ssize_t)OutBuf.offBuf)
421 rc = 0;
422 else
423 rc = err(1, "error writing %lu bytes to '%s'", (unsigned long)OutBuf.offBuf, pszFilename);
424 if (close(fd) < 0)
425 rc = err(1, "error closing '%s'", pszFilename);
426 }
427 else
428 rc = err(1, "failed to open '%s'", pszFilename);
429 free(OutBuf.pszBuf);
430 }
431 else
432 rc = errx(1, "out of memory!");
433 return rc;
434}
435
Note: See TracBrowser for help on using the repository browser.

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