VirtualBox

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

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

kmkbuiltin: funnel output thru output.c (usually via err.c).

  • 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 3192 2018-03-26 20:25:56Z 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_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 <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_STANDALONE) && 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 */
175int kmk_builtin_append(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx, struct child *pChild, pid_t *pPidSpawned)
176{
177#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
178 static const char s_szNewLine[] = "\r\n";
179#else
180 static const char s_szNewLine[] = "\n";
181#endif
182 KMKBUILTINAPPENDBUF OutBuf = { NULL, 0, 0, 0 };
183 const char *pszFilename;
184 int rc = 88;
185 int i;
186 int fFirst;
187 int fNewline = 0;
188 int fNoTrailingNewline = 0;
189 int fTruncate = 0;
190 int fDefine = 0;
191 int fVariables = 0;
192 int fCommands = 0;
193#ifndef KMK_BUILTIN_STANDALONE
194 int fLookForInserts = 0;
195#else
196 (void)pChild; (void)pPidSpawned;
197#endif
198
199 /*
200 * Parse options.
201 */
202 i = 1;
203 while (i < argc
204 && argv[i][0] == '-'
205 && argv[i][1] != '\0' /* '-' is a file */
206 && strchr("-cdinNtv", argv[i][1]) /* valid option char */
207 )
208 {
209 char *psz = &argv[i][1];
210 if (*psz != '-')
211 {
212 do
213 {
214 switch (*psz)
215 {
216 case 'c':
217 if (fVariables)
218 {
219 errx(pCtx, 1, "Option '-c' clashes with '-v'.");
220 return kmk_builtin_append_usage(argv[0], stderr);
221 }
222#ifndef KMK_BUILTIN_STANDALONE
223 fCommands = 1;
224 break;
225#else
226 errx(pCtx, 1, "Option '-c' isn't supported in external mode.");
227 return kmk_builtin_append_usage(argv[0], stderr);
228#endif
229 case 'd':
230 if (fVariables)
231 {
232 errx(pCtx, 1, "Option '-d' must come before '-v'!");
233 return kmk_builtin_append_usage(argv[0], stderr);
234 }
235 fDefine = 1;
236 break;
237 case 'i':
238 if (fVariables || fCommands)
239 {
240 errx(pCtx, 1, fVariables ? "Option '-i' clashes with '-v'." : "Option '-i' clashes with '-c'.");
241 return kmk_builtin_append_usage(argv[0], stderr);
242 }
243#ifndef KMK_BUILTIN_STANDALONE
244 fLookForInserts = 1;
245 break;
246#else
247 errx(pCtx, 1, "Option '-C' isn't supported in external mode.");
248 return kmk_builtin_append_usage(argv[0], stderr);
249#endif
250 case 'n':
251 fNewline = 1;
252 break;
253 case 'N':
254 fNoTrailingNewline = 1;
255 break;
256 case 't':
257 fTruncate = 1;
258 break;
259 case 'v':
260 if (fCommands)
261 {
262 errx(pCtx, 1, "Option '-v' clashes with '-c'.");
263 return kmk_builtin_append_usage(argv[0], stderr);
264 }
265#ifndef KMK_BUILTIN_STANDALONE
266 fVariables = 1;
267 break;
268#else
269 errx(pCtx, 1, "Option '-v' isn't supported in external mode.");
270 return kmk_builtin_append_usage(argv[0], stderr);
271#endif
272 default:
273 errx(pCtx, 1, "Invalid option '%c'! (%s)", *psz, argv[i]);
274 return kmk_builtin_append_usage(argv[0], stderr);
275 }
276 } while (*++psz);
277 }
278 else if (!strcmp(psz, "-help"))
279 {
280 kmk_builtin_append_usage(argv[0], stdout);
281 return 0;
282 }
283 else if (!strcmp(psz, "-version"))
284 return kbuild_version(argv[0]);
285 else
286 break;
287 i++;
288 }
289
290 /*
291 * Take down the filename.
292 */
293 if (i + fDefine < argc)
294 pszFilename = argv[i++];
295 else
296 {
297 if (i <= argc)
298 errx(pCtx, 1, "missing filename!");
299 else
300 errx(pCtx, 1, "missing define name!");
301 return kmk_builtin_append_usage(argv[0], stderr);
302 }
303
304 /* Start of no-return zone! */
305
306 /*
307 * Start define?
308 */
309 if (fDefine)
310 {
311 write_to_buf(&OutBuf, STR_TUPLE("define "));
312 string_to_buf(&OutBuf, argv[i]);
313 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
314 i++;
315 }
316
317 /*
318 * Append the argument strings to the file
319 */
320 fFirst = 1;
321 for (; i < argc; i++)
322 {
323 const char *psz = argv[i];
324 size_t cch = strlen(psz);
325 if (!fFirst)
326 {
327 if (fNewline)
328 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
329 else
330 write_to_buf(&OutBuf, STR_TUPLE(" "));
331 }
332#ifndef KMK_BUILTIN_STANDALONE
333 if (fCommands)
334 {
335 char *pszOldBuf;
336 unsigned cchOldBuf;
337 char *pchEnd;
338
339 install_variable_buffer(&pszOldBuf, &cchOldBuf);
340
341 pchEnd = func_commands(variable_buffer, &argv[i], "commands");
342 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
343
344 restore_variable_buffer(pszOldBuf, cchOldBuf);
345 }
346 else if (fVariables)
347 {
348 struct variable *pVar = lookup_variable(psz, cch);
349 if (!pVar)
350 continue;
351 if ( !pVar->recursive
352 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
353 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
354 else
355 {
356 char *pszExpanded = allocated_variable_expand(pVar->value);
357 string_to_buf(&OutBuf, pszExpanded);
358 free(pszExpanded);
359 }
360 }
361 else if (fLookForInserts && strncmp(psz, "--insert-command=", 17) == 0)
362 {
363 char *pszOldBuf;
364 unsigned cchOldBuf;
365 char *pchEnd;
366
367 install_variable_buffer(&pszOldBuf, &cchOldBuf);
368
369 psz += 17;
370 pchEnd = func_commands(variable_buffer, (char **)&psz, "commands");
371 write_to_buf(&OutBuf, variable_buffer, pchEnd - variable_buffer);
372
373 restore_variable_buffer(pszOldBuf, cchOldBuf);
374 }
375 else if (fLookForInserts && strncmp(psz, "--insert-variable=", 18) == 0)
376 {
377 struct variable *pVar = lookup_variable(psz + 18, cch);
378 if (!pVar)
379 continue;
380 if ( !pVar->recursive
381 || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
382 write_to_buf(&OutBuf, pVar->value, pVar->value_length);
383 else
384 {
385 char *pszExpanded = allocated_variable_expand(pVar->value);
386 string_to_buf(&OutBuf, pszExpanded);
387 free(pszExpanded);
388 }
389 }
390 else
391#endif
392 write_to_buf(&OutBuf, psz, cch);
393 fFirst = 0;
394 }
395
396 /*
397 * End the define?
398 */
399 if (fDefine)
400 {
401 if (fFirst)
402 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
403 write_to_buf(&OutBuf, STR_TUPLE("endef"));
404 }
405
406 /*
407 * Add final newline (unless supressed) and check for errors.
408 */
409 if (!fNoTrailingNewline)
410 write_to_buf(&OutBuf, STR_TUPLE(s_szNewLine));
411
412 /*
413 * Write the buffer (unless we ran out of heap already).
414 */
415#if !defined(KMK_BUILTIN_STANDALONE) && defined(KBUILD_OS_WINDOWS) && defined(CONFIG_NEW_WIN_CHILDREN)
416 if (!OutBuf.fOutOfMemory)
417 {
418 rc = MkWinChildCreateAppend(pszFilename, &OutBuf.pszBuf, OutBuf.offBuf, fTruncate, pChild, pPidSpawned);
419 if (rc != 0)
420 rc = errx(pCtx, rc, "MkWinChildCreateAppend failed: %u", rc);
421 if (OutBuf.pszBuf)
422 free(OutBuf.pszBuf);
423 }
424 else
425#endif
426 if (!OutBuf.fOutOfMemory)
427 {
428 int fd = open(pszFilename,
429 fTruncate ? O_WRONLY | O_TRUNC | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY
430 : O_WRONLY | O_APPEND | O_CREAT | MY_O_NOINHERIT | MY_O_BINARY,
431 0666);
432 if (fd >= 0)
433 {
434 ssize_t cbWritten = write(fd, OutBuf.pszBuf, OutBuf.offBuf);
435 if (cbWritten == (ssize_t)OutBuf.offBuf)
436 rc = 0;
437 else
438 rc = err(pCtx, 1, "error writing %lu bytes to '%s'", (unsigned long)OutBuf.offBuf, pszFilename);
439 if (close(fd) < 0)
440 rc = err(pCtx, 1, "error closing '%s'", pszFilename);
441 }
442 else
443 rc = err(pCtx, 1, "failed to open '%s'", pszFilename);
444 free(OutBuf.pszBuf);
445 }
446 else
447 rc = errx(pCtx, 1, "out of memory for output buffer! (%u needed)", OutBuf.offBuf + 1);
448 return rc;
449}
450
451#ifdef KMK_BUILTIN_STANDALONE
452int main(int argc, char **argv, char **envp)
453{
454 KMKBUILTINCTX Ctx = { "kmk_append", NULL };
455 return kmk_builtin_append(argc, argv, envp, &Ctx, NULL, NULL);
456}
457#endif
458
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