VirtualBox

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

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

kBuiltinOptEnvDuplicat, append: Fixed allocation bug in kBuiltinOptEnvDuplicate; improved out of memory message so.

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