VirtualBox

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

Last change on this file since 3387 was 3246, checked in by bird, 5 years ago

kmk_append: Debian build fix.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use